Parsing list of string containing a delimiter to tree structure - c#

So I have a list of strings like so:
var drinks = new List(){"Drinks", " * ", "Rum", "Captain Morgan", "Kraken", " * ", "Whiskey",
"Laphroaig"}
It needs to return the following:
*Drinks
*Drinks * Rum
*Drinks * Rum * Captain Morgan
*Drinks * Rum * Kraken
*Drinks * Whiskey
*Drinks * Whiskey * Laphroaig
So as seen, anytime a * is encountered, the next string would be treated as a child under the root. So here, Rum would fall under Drinks and Captain Morgan and Kraken would fall under Rum. Whiskey would fall under Drinks and Laphroaig would fall under whiskey.
I know it has to be some sort of tree structure and the only thing I have right now is this:
private static Drink GroupDrinks(List<string> drinkNames)
{
var drink = new Drink() { Children = new List<Drink>() };
foreach (var drinkName in drinkNames)
{
if (drinkName != "*")
{
drink.Name = drinkName;
drinkNames.RemoveAt(0);
}
else
{
drinkNames.RemoveAt(0);
drink.Children.Add(GroupDrinks(drinkNames));
}
}
return drink;
}
I figured I'd need to do some kind of recursion and maybe remove the character so it doesn't affect the next iteration but this clearly isn't working. Any tips would be great.

I am not sure if this code work for you but it is tested as your expected output:
Declaration:
List<Drink> lstdrink = new List<Drink>();
public List<FinalDrink> lstFinalDrink = new List<FinalDrink>();
Class:
public class FinalDrink
{
public string name { get; set; }
}
public class Drink
{
public string name { get; set; }
public int Tag { get; set; }
}
Set Up the Value:
public List<Drink> SetUpTheValue()
{
var drinks = new List<string> { "Drinks", " * ", "Rum", "Captain Morgan", "Kraken", " * ", "Whiskey", "Laphroaig" };
var repl = drinks.Select(s => s.Replace('*', ' ')).ToList();
string tag = string.Empty;
Drink drk = new Drink();
lstdrink = new List<Drink>();
for (int i = 0; i < repl.Count; i++)
{
if (i == 0)
{
drk = new Drink();
drk.name = repl[i];
drk.Tag = 1;
lstdrink.Add(drk);
tag = repl[i];
continue;
}
if (tag.Trim().Length == 0)
{
drk = new Drink();
drk.name = repl[i];
drk.Tag = 2;
lstdrink.Add(drk);
tag = repl[i];
continue;
}
if (repl[i].ToString().Trim().Length > 0)
{
drk = new Drink();
drk.name = repl[i];
drk.Tag = 0;
lstdrink.Add(drk);
tag = repl[i];
}
tag = repl[i];
}
return lstdrink;
}
Group Drinks:
public List<FinalDrink> GroupDrinks(List<Drink> drinkNames)
{
lstFinalDrink = new List<FinalDrink>();
FinalDrink fDrink = new FinalDrink();
var GetFirst = drinkNames.Where(x => x.Tag == 1).ToList();
fDrink.name = GetFirst[0].name.ToString();
lstFinalDrink.Add(fDrink);
var Content = drinkNames.Where(x => x.Tag != 1).ToList();
string itrVal = string.Empty;
int prev = 0;
string hcur = string.Empty;
for (int i = 0; i < Content.Count(); i++)
{
if (Content[i].Tag == 2)
{
hcur = GetFirst[0].name + " * " + Content[i].name;
fDrink = new FinalDrink();
itrVal = GetFirst[0].name + " * " + Content[i].name;
fDrink.name = itrVal;
lstFinalDrink.Add(fDrink);
prev = Content[i].Tag;
itrVal = string.Empty;
}
else
{
fDrink = new FinalDrink();
itrVal = hcur + " * " + Content[i].name;
fDrink.name = itrVal;
lstFinalDrink.Add(fDrink);
prev = Content[i].Tag;
itrVal = string.Empty;
}
}
return lstFinalDrink;
}
Execution:
private void button1_Click(object sender, EventArgs e)
{
if (SetUpTheValue().Count() > 0)
{
GroupDrinks(lstdrink);
}
}
The GroupDrinks return List<FinalDrink> this is the final result.
It is depend on you to modify the result
This Code will return the expected output as you added from above.

Related

C# Loop over all possible values, when not found increase value and continue

I need to send a request for every supplier code to a webservice (i know that sounds crazy but the owner designed it this way).
The supplier code format is:
30X1X1XXXXX1
You can check what i did so far (github link: https://github.com/rareba/SiapWebServices_Library/blob/master/SiapWebServices_Library/SiapWebServicesFornitore.cs)
public static List<StructAnaFornitoreOut> Get_All_Suppliers(WebServicesFornitoreClient client, StructLogin loginCredentials, string showSuspended = "N", string searchType = "R")
{
var supplier_list = new List<StructAnaFornitoreOut>();
var supplier_retrived = new StructAnaFornitoreOut
{
esito = new StructEsito
{
stato = "OK",
}
};
int KO_Count = 0;
int conto = 1;
int sottoconto = 1;
int codice = 0;
string codFornitore = Generate_Supplier_Code_String(codice, sottoconto, conto);
var search = new StructAnaFornitoreIn
{
IAnaFornitore = new StructAnaFornitore
{
codice = codFornitore
},
IAzione = "C",
//vModRicerca = new string[] { "COD_FIS","PAR_IVA","ALT_SIS" }
};
while (KO_Count < 10)
{
while (KO_Count < 10)
{
while (KO_Count < 10)
{
supplier_retrived = client.gestioneAnagraficaFornitore(loginCredentials, search);
if (supplier_retrived.esito.stato == "KO")
{
KO_Count++;
}
else
{
supplier_list.Add(supplier_retrived);
codFornitore = Generate_Supplier_Code_String(codice, sottoconto, conto);
Console.WriteLine(codFornitore);
codice++;
search.IAnaFornitore.codice = codFornitore;
}
}
KO_Count = 0;
sottoconto++;
}
KO_Count = 0;
conto++;
}
return supplier_list;
}
// Returns a supplier code string increased by 1
public static string Generate_Supplier_Code_String(int codice = 0, int sottoconto = 1, int conto = 1, string mastro = "30")
{
codice++;
string string_conto = " ";
string string_sottoconto = " ";
string string_codice = " ";
if (conto > 9)
{
string_conto = "";
}
if (sottoconto > 9)
{
string_sottoconto = "";
}
if (codice > 9)
{
string_codice = " ";
}
else if (codice > 99)
{
string_codice = " ";
}
else if (codice > 999)
{
string_codice = " ";
}
else if (codice > 9999)
{
string_codice = " ";
}
else if (codice > 99999)
{
string_codice = " ";
}
else if (codice >= 999999)
{
string_codice = "";
}
string customercode = mastro + string_conto + conto + string_sottoconto + sottoconto + string_codice + codice;
return customercode;
}
However this doesn't work at all: it stops at supplier 30 1 1 100 and starts increasing sottoconto like there is no tomorrow.
The idea should be to do something like:
- Get the supplier data
- Increase codice by 1
- If for 10 times you get nothing (esito.status = "KO") then increase sottoconto and start again from codice = 1
- If after incresing sottoconto you still get nothing for 10 times then increase conto and start again from codice = 0

Custom OrderBy on a List based on a string property value

I have the following class :
public class Document
{
public string DocumentSection { get; set; }
public string DocumentName { get; set; }
}
and I would like to order the following list based on the DocumentSection property:
List<Document> documents = new List<Document>();
documents.Add(new Document { DocumentSection = "Section One", DocumentName = "doc1" });
documents.Add(new Document { DocumentSection = "Section Two", DocumentName = "doc1123" });
documents.Add(new Document { DocumentSection = "Section Three", DocumentName = "doc113" });
documents.Add(new Document { DocumentSection = "Section Four", DocumentName = "doc123" });
documents.Add(new Document { DocumentSection = "Section Five", DocumentName = "doc11" });
In theory I know that I should implement IComparer to obtain that, but this is where the difficulty comes in, I am not very sure how can I achieve that on a general level ... what is the best solution to achieve this ordering ?
Try this:
var orderedList = documents.OrderBy(r => GetOrder(r.DocumentSection));
and the GetOrder() method is:
Public Static int GetOrder(string _arg)
{
switch (_arg)
{
case 'Section One':
return 1;
case 'Section Two':
return 2;
case 'Section Three':
return 3;
.
.
.
default:
return int.MaxValue;
}
}
easiest way is to use Linq :
List<Order> SortedList = objListOrder.OrderBy(o=>o.Order).ToList();
Sample :
List<Document> SortedList = documents.OrderBy(o=>o.DocumentName).ToList();
Long Answer
you need to customize above order method but you don't need to type all number in this code
with wordify method you can convert number to word and
with normalize_number method we normalize both result word and first word so we can check result word with first word
we have a loop and we don't like to create word always so we can use from Dictionary and check number, we do this in get_wordify method.
now with SetOrder we can find correct order
So Just Use from this code :
private int current_order = 0;
private int SetOrder(string _arg)
{
string number = _arg.ToLower()
.Replace("section", "")
.TrimStart(' ')
.TrimEnd(' ');
number = normalize_number(number);
for (int i = 0;
//you can limit loop here (i<99999) or not !
; i++)
{
string wordify_number = get_wordify(i);
if (wordify_number == number)
{
//if all number is available return i+1
//return i + 1;
//otherwise
current_order++;
return current_order;
}
}
}
private string normalize_number(string number)
{
number = number.Replace("-", " ")
.Replace("_", " ")
.Replace(",", " ");
//also you can replace "and" if you want
//.Replace(" ", " ");
//regex is better for find and replace multi space
RegexOptions options = RegexOptions.None;
Regex regex = new Regex("[ ]{2,}", options);
number = regex.Replace(number, " ");
return number;
}
Dictionary<int, string> Numbers_dic = new Dictionary<int, string>();
private string get_wordify(int i)
{
string wordify_number = "";
if (Numbers_dic.ContainsKey(i))
{
wordify_number = Numbers_dic[i];
}
else {
wordify_number = wordify(i);
wordify_number = normalize_number(wordify_number);
Numbers_dic.Add(i, wordify_number);
}
return wordify_number;
}
private string wordify(decimal number)
{
if (number == 0) return "zero";
var units = " one two three four five six seven eight nine".Split();
var teens = " eleven twelve thir# four# fif# six# seven# eigh# nine#".Replace("#", "teen").Split();
var tens = " ten twenty thirty forty fifty sixty seventy eighty ninety".Split();
var thou = " thousand m# b# tr# quadr# quint# sext# sept# oct#".Replace("#", "illion").Split();
var minus_str = (number < 0) ? "minus " : "";
var res_number = "";
var p = 0;
number = Math.Abs(number);
while (number > 0)
{
int b = (int)(number % 1000);
if (b > 0)
{
var h = (b / 100);
var t = (b - h * 100) / 10;
var u = (b - h * 100 - t * 10);
var str = ((h > 0) ? units[h] + " hundred" + ((t > 0 | u > 0) ? " and " : "") : "")
+ ((t > 0) ? (t == 1 && u > 0) ? teens[u] : tens[t] + ((u > 0) ? "-" : "") : "")
+ ((t != 1) ? units[u] : "");
str = (((number > 1000) && (h == 0) && (p == 0)) ? " and " : (number > 1000) ? ", " : "") + str;
res_number = str + " " + thou[p] + res_number;
}
number = number / 1000;
if (number < 1)
{
break;
}
p++;
}
return minus_str + res_number;
}
Usage :
private void Do_order()
{
var orderedList = documents.OrderBy(r => SetOrder(r.DocumentSection));
}

Scrolling DataGridView with buttons

Hello I have a code where I inside of a datagridview generate buttons with data from sql server database. But now I want to scroll them through buttons. I tried lots of things all gives me error already saw a post about this but nothing worked for me can someone help.
<-----------------------------------My code------------------------------------->
Methods to fill the datagridview:
public void TabelaFuncionario()
{
try
{
BDfuncionarios = new DataTable();
string cmd = "My select string";
var adpt = fConexao.GetDataAdapter(cmd);
BDfuncionarios.Clear();
adpt.Fill(BDfuncionarios);
}
catch (Exception r)
{
MessageBox.Show(r.Message);
}
}
public void BotaoFuncionario()
{
try
{
TabelaFuncionario();
PosXartigo = 1;
PosYartigo = 1;
//Apagar o painel todo
dataGridView1.Controls.Clear();
foreach (DataRow row in BDfuncionarios.Rows)
{
int posicaoX = ((PosXartigo - 1) * Gap_Xartigo) + xInicial + (Largura_BotaoArtigo * (PosXartigo - 1));
if (posicaoX > maximoxArtigo)
{
PosYartigo++; PosXartigo = 1;
}
else
{
PosXartigo = PosXartigo != 1 ? PosXartigo++ : 1;
}
int PontoX = ((PosXartigo - 1) * Gap_Xartigo) + xInicial + (Largura_BotaoArtigo * (PosXartigo - 1));
int PontoY = ((PosYartigo - 1) * Gap_Yartigo) + yInicial + (Altura_BotaoArtigo * (PosYartigo - 1));
Button bt1 = new Button();
bt1.Location = new Point(PontoX, PontoY);
Mo mo = new Mo();
mo.codmo = (int)row["Something"];
mo.nome_func = (string)row["Something"];
bt1.Name = "Botao" + NBotoes.ToString();
bt1.Height = Altura_BotaoArtigo;
bt1.Width = Largura_BotaoArtigo;
bt1.BackColor = Color.Tan;
bt1.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
bt1.ForeColor = Color.Black;
bt1.Text = mo.nome_func;
bt1.Tag = mo;
bt1.FlatStyle = FlatStyle.Popup;
bt1.Click += btArtigo_click;
dataGridView1.Controls.Add(bt1);
NBotoes++;
PosXartigo++;
}
}
catch (Exception r)
{
MessageBox.Show(r.Message);
}
}
Image of my form (don't know if it helps):
http://imgur.com/f5G25nX
<--------------------------EDITED--------------------------------->
i have tried things like this :https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.rowcount(v=vs.110).aspx
Gives me out of range or something like that
And tried this just now
int row = dataGridView1.RowCount;
MessageBox.Show(row+"");
And it displays me 0; how can i have buttons inside my grid but have 0 rows?
I solved the problem using this panel instead of datagrid for what u wanted it was much better the code will be below:
Methods:
public void TabelaFuncionario()
{
try
{
BDfuncionarios = new DataTable();
string cmd = "your select";
var adpt = fConexao.GetDataAdapter(cmd);
BDfuncionarios.Clear();
adpt.Fill(BDfuncionarios);
}
catch (Exception r)
{
MessageBox.Show(r.Message);
}
}
public void BotaoFuncionario()
{
try
{
TabelaFuncionario();
PosXartigo = 1;
PosYartigo = 1;
//Apagar o painel todo
panel2.Controls.Clear();
foreach (DataRow row in BDfuncionarios.Rows)
{
int posicaoX = ((PosXartigo - 1) * Gap_Xartigo) + xInicial + (Largura_BotaoArtigo * (PosXartigo - 1));
if (posicaoX > maximoxArtigo)
{
PosYartigo++; PosXartigo = 1;
}
else
{
PosXartigo = PosXartigo != 1 ? PosXartigo++ : 1;
}
int PontoX = ((PosXartigo - 1) * Gap_Xartigo) + xInicial + (Largura_BotaoArtigo * (PosXartigo - 1));
int PontoY = ((PosYartigo - 1) * Gap_Yartigo) + yInicial + (Altura_BotaoArtigo * (PosYartigo - 1));
Button bt1 = new Button();
bt1.Location = new Point(PontoX, PontoY);
Mo mo = new Mo();
mo.codmo = (int)row["Var1"];
mo.nome_func = (string)row["Var2"];
bt1.Name = "Botao" + NBotoes.ToString();
bt1.Height = Altura_BotaoArtigo;
bt1.Width = Largura_BotaoArtigo;
bt1.BackColor = Color.Tan;
bt1.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Bold);
bt1.ForeColor = Color.Black;
bt1.Text = mo.nome_func;
bt1.Tag = mo;
bt1.FlatStyle = FlatStyle.Popup;
bt1.Click += btFuncionario_click;
panel2.Controls.Add(bt1);
NBotoes++;
PosXartigo++;
}
}
catch (Exception r)
{
MessageBox.Show(r.Message);
}
}
Now the PainelExtension class:
public static class PanelExtension
{
public static void ScrollDown(this Panel p, int pos)
{
//pos passed in should be positive
using (Control c = new Control() { Parent = p, Height = 1, Top = p.ClientSize.Height + pos })
{
p.ScrollControlIntoView(c);
}
}
public static void ScrollUp(this Panel p, int pos)
{
//pos passed in should be negative
using (Control c = new Control() { Parent = p, Height = 1, Top = pos })
{
p.ScrollControlIntoView(c);
}
}
}
The up and down button click:
private void upbt_Click(object sender, EventArgs e)
{
if (i >= 0) i = -1;
panel2.ScrollUp(i=i-30);
}
private void downbt_Click(object sender, EventArgs e)
{
if (i < 0) i = 0;
panel2.ScrollDown(i=i+20);
}
I got it to work like this maybe there were other ways to do it i choose this one.

How do I display exact number of Questions only

In Question set i am getting all questions with particular (topicID,Marks). I am displaying Question randomly for totqsn i.e. total number of questions to display say 10 questions ,I am storing Count for number of questions of particular marks(1,2,3,4) in this int variables mark1Qsn,mark2Qsn,mark3Qsn,mark4Qsn respectively ,
using below code i am able to display Qustions from QuestionSet(say contains 34 qustions with (TopicID,marks)) for totqsn(say display 10 questions randomly from QuestionSet).My problem is
How can i display total 10 Question in which 3 questions of 1 mark,3 questions of 2mark,1 question of 3marks,3 questions of 4 marks i.e.
totqsn(10 questions= 3 qsn_of_mark1 + 3 qsn_of_mark2 + 1 qsn_of_mark3 + 3 qsn_of_mark3)
public partial class GroupExmStart : Form
{
DBHandling db = new DBHandling();
string GrpID = "";
string TopiID = "";
int totQsn = 0;
int mark1qsn = 0;
int mark2Qsn = 0;
int mark3Qsn = 0;
int mark4Qsn = 0;
int tik = 0;
string QuestionSet = "";
static Random _r = new Random();
string[] randomQsn = null;
string[] QAndA = null;
public GroupExmStart(string GroupName, string DurationID)
{
InitializeComponent();
totQsn = Convert.ToInt16(conf[0]);
mark1qsn = Convert.ToInt16(conf[3]);//this variable contains number of question to be display of mark 1
mark2Qsn = Convert.ToInt16(conf[4]);
mark3Qsn = Convert.ToInt16(conf[5]);
mark4Qsn = Convert.ToInt16(conf[6]);
QuestionSet = db.GetQuestions(TopiID, "1");
QuestionSet = QuestionSet + db.GetQuestions(TopiID, "2");
QuestionSet = QuestionSet + db.GetQuestions(TopiID, "3");
QuestionSet = QuestionSet + db.GetQuestions(TopiID, "4");
int z = Quiz(QuestionSet);
foreach (string qa in QAndA.OrderBy(i => _random.Next()))
{
if (qa != null)
if (qa.IndexOf('~') != -1)
{
randomQsn[count] = qa;
count++;
if (count == totQsn)
break;
}
}
int Quiz(string data)
{
string[] words = data.Split('$');
randomQsn = new string[totQsn + 1];
QAndA = new string[words.Length + 1];
for (int i = 0; i < words.Length; i++)
{
QAndA[i] = words[i];
}
return 0;
}
}
}
GetQuestions method accessing from DBHandling class
public string GetQuestions(string TopicID, string Marks)
{
string data = "";
try
{
string sql = "select QID,Question,Opt1,Opt2,Opt3,Opt4,AnsOp,Marks from Questions where TopicID IN(" + TopicID + ") and Marks=" + Marks;
cmd = new OleDbCommand(sql, acccon);
rs = cmd.ExecuteReader();
while (rs.Read())
{
data = data + rs[0].ToString() + "~" + rs[1].ToString() + "~" + rs[2].ToString() + "~" + rs[3].ToString() + "~" + rs[4].ToString() + "~" + rs[5].ToString() + "~" + rs[6].ToString() + "~" + rs[7].ToString() + "$";
}
}
catch (Exception err)
{
MessageBox.Show(err.Message.ToString());
}
return data;
}
Thanks in advance for any help
Completely untested and very messy but...
public class Question
{
public string Id { get; set; }
public string Text { get; set; }
public string Option1 { get; set; }
public string Option2 { get; set; }
public string Option3 { get; set; }
public string Option4 { get; set; }
public string AnswerOption { get; set; }
public int Marks { get; set; }
}
public IEnumerable<Question> GetQuestions(string topicId, int marks)
{
string sql = "select QID,Question,Opt1,Opt2,Opt3,Opt4,AnsOp,Marks from Questions where TopicID IN(" +
topicId + ") and Marks=" + marks.ToString();
var cmd = new OleDbCommand(sql, new OleDbConnection(""));
var rs = cmd.ExecuteReader();
if (rs != null)
{
while (rs.Read())
{
yield return
new Question
{
Id = rs[0].ToString(),
Text = rs[1].ToString(),
Option1 = rs[2].ToString(),
Option2 = rs[3].ToString(),
Option3 = rs[4].ToString(),
Option4 = rs[5].ToString(),
AnswerOption = rs[6].ToString(),
Marks = marks
};
}
}
}
public void Foo()
{
var totQsn = Convert.ToInt16(conf[0]); // isn't this just the sum of everything else?
var mark1qsn = Convert.ToInt16(conf[3]); //this variable contains number of question to be display of mark 1
var mark2qsn = Convert.ToInt16(conf[4]);
var mark3Qsn = Convert.ToInt16(conf[5]);
var mark4Qsn = Convert.ToInt16(conf[6]);
var mark1questionSet = GetQuestions(topicId, 1).ToList();
var mark2questionSet = GetQuestions(topicId, 2).ToList();
// etc
var finalQuestions = new List<Question>();
for (int i = 0; i < mark1qsn; i++)
{
var setIndex = _random.Next(mark1questionSet.Count);
finalQuestions.Add(mark1questionSet[setIndex]);
mark1questionSet.RemoveAt(setIndex);
}
for (int i = 0; i < mark2qsn; i++)
{
var setIndex = _random.Next(mark2questionSet.Count);
finalQuestions.Add(mark2questionSet[setIndex]);
mark2questionSet.RemoveAt(setIndex);
}
// etc - put this into a method or something
}

What C# template engine that has clean separation between HTML and control code?

What C# template engine
that uses 'pure' HTML having only text and markers
sans any control flow like if, while, loop or expressions,
separating html from control code ?
Below is the example phone book list code,
expressing how this should be done:
string html=#"
<html><head><title>#title</title></head>
<body>
<table>
<tr>
<td> id</td> <td> name</td> <td> sex</td> <td>phones</td>
</tr><!--#contacts:-->
<tr>
<td>#id</td> <td>#name</td> <td>#sex</td>
<td>
<!--#phones:-->#phone <br/>
<!--:#phones-->
</td>
</tr><!--:#contacts-->
</table>
</body>
</html>";
var contacts = from c in db.contacts select c;
Marker m = new Marker(html);
Filler t = m.Mark("title");
t.Set("Phone book");
Filler c = m.Mark("contacts", "id,name,sex");
// **foreach** expressed in code, not in html
foreach(var contact in contacts) {
int id = contact.id;
c.Add(id, contact.name, contact.sex);
Filler p = c.Mark("phones", "phone");
var phones = from ph in db.phones
where ph.id == id
select new {ph.phone};
if (phones.Any()) {
foreach(var ph in phones) {
p.Add(ph);
}
} else {
fp.Clear();
}
}
Console.Out.WriteLine(m.Get());
Use this code:
Templet.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace templaten.com.Templaten
{
public class tRange
{
public int head, toe;
public tRange(int _head, int _toe)
{
head = _head;
toe = _toe;
}
}
public enum AType
{
VALUE = 0,
NAME = 1,
OPEN = 2,
CLOSE = 3,
GROUP = 4
}
public class Atom
{
private AType kin;
private string tag;
private object data;
private List<Atom> bag;
public Atom(string _tag = "",
AType _kin = AType.VALUE,
object _data = null)
{
tag = _tag;
if (String.IsNullOrEmpty(_tag))
_kin = AType.GROUP;
kin = _kin;
if (_kin == AType.GROUP)
bag = new List<Atom>();
else
bag = null;
data = _data;
}
public AType Kin
{
get { return kin; }
}
public string Tag
{
get { return tag; }
set { tag = value; }
}
public List<Atom> Bag
{
get { return bag; }
}
public object Data
{
get { return data; }
set { data = value; }
}
public int Add(string _tag = "",
AType _kin = AType.VALUE,
object _data = null)
{
if (bag != null)
{
bag.Add(new Atom(_tag, _kin, _data));
return bag.Count - 1;
}
else
{
return -1;
}
}
}
public class Templet
{
private string content;
string namepat = "\\w+";
string justName = "(\\w+)";
string namePre = "#";
string namePost = "";
string comment0 = "\\<!--\\s*";
string comment1 = "\\s*--\\>";
private Atom tokens; // parsed contents
private Dictionary<string, int> iNames; // name index
private Dictionary<string, tRange> iGroups; // groups index
private Atom buffer; // output buffer
private Dictionary<string, int> _iname; // output name index
private Dictionary<string, tRange> _igroup; // output index
public Templet(string Content = null)
{
Init(Content);
}
private int[] mark(string[] names, string group)
{
if (names == null || names.Length < 1) return null;
tRange t = new tRange(0, buffer.Bag.Count - 1);
if (group != null)
{
if (!_igroup.ContainsKey(group)) return null;
t = _igroup[group];
}
int[] marks = new int[names.Length];
for (int i = 0; i < marks.Length; i++)
marks[i] = -1;
for (int i = t.head; i <= t.toe; i++)
{
if (buffer.Bag[i].Kin == AType.NAME)
{
for (int j = 0; j < names.Length; j++)
{
if (String.Compare(
names[j],
buffer.Bag[i].Tag,
true) == 0)
{
marks[j] = i;
break;
}
}
}
}
return marks;
}
public Filler Mark(string group, string names)
{
Filler f = new Filler(this, names);
f.di = mark(f.names, group);
f.Group = group;
tRange t = null;
if (_igroup.ContainsKey(group)) t = _igroup[group];
f.Range = t;
return f;
}
public Filler Mark(string names)
{
Filler f = new Filler(this, names);
f.di = mark(f.names, null);
f.Group = "";
f.Range = null;
return f;
}
public void Set(int[] locations, object[] x)
{
int j = Math.Min(x.Length, locations.Length);
for (int i = 0; i < j; i++)
{
int l = locations[i];
if ((l >= 0) && (buffer.Bag[l] != null))
buffer.Bag[l].Data = x[i];
}
}
public void New(string group, int seq = 0)
{
// place new group copied from old group just below it
if (!( iGroups.ContainsKey(group)
&& _igroup.ContainsKey(group)
&& seq > 0)) return;
tRange newT = null;
tRange t = iGroups[group];
int beginRange = _igroup[group].toe + 1;
for (int i = t.head; i <= t.toe; i++)
{
buffer.Bag.Insert(beginRange,
new Atom(tokens.Bag[i].Tag,
tokens.Bag[i].Kin,
tokens.Bag[i].Data));
beginRange++;
}
newT = new tRange(t.toe + 1, t.toe + (t.toe - t.head + 1));
// rename past group
string pastGroup = group + "_" + seq;
t = _igroup[group];
buffer.Bag[t.head].Tag = pastGroup;
buffer.Bag[t.toe].Tag = pastGroup;
_igroup[pastGroup] = t;
// change group indexes
_igroup[group] = newT;
}
public void ReMark(Filler f, string group)
{
if (!_igroup.ContainsKey(group)) return;
Map(buffer, _iname, _igroup);
f.di = mark(f.names, group);
f.Range = _igroup[group];
}
private static void Indexing(string aname,
AType kin,
int i,
Dictionary<string, int> dd,
Dictionary<string, tRange> gg)
{
switch (kin)
{
case AType.NAME: // index all names
dd[aname] = i;
break;
case AType.OPEN: // index all groups
if (!gg.ContainsKey(aname))
gg[aname] = new tRange(i, -1);
else
gg[aname].head = i;
break;
case AType.CLOSE:
if (!gg.ContainsKey(aname))
gg[aname] = new tRange(-1, i);
else
gg[aname].toe = i;
break;
default:
break;
}
}
private static void Map(Atom oo,
Dictionary<string, int> dd,
Dictionary<string, tRange> gg)
{
for (int i = 0; i < oo.Bag.Count; i++)
{
string aname = oo.Bag[i].Tag;
Indexing(oo.Bag[i].Tag, oo.Bag[i].Kin, i, dd, gg);
}
}
public void Init(string Content = null)
{
content = Content;
tokens = new Atom("", AType.GROUP);
iNames = new Dictionary<string, int>();
iGroups = new Dictionary<string, tRange>();
// parse content into tokens
string namePattern = namePre + namepat + namePost;
string patterns =
"(?<var>" + namePattern + ")|" +
"(?<head>" + comment0 + namePattern + ":" + comment1 + ")|" +
"(?<toe>" + comment0 + ":" + namePattern + comment1 + ")";
Regex jn = new Regex(justName, RegexOptions.Compiled);
Regex r = new Regex(patterns, RegexOptions.Compiled);
MatchCollection ms = r.Matches(content);
int pre = 0;
foreach (Match m in ms)
{
tokens.Add(content.Substring(pre, m.Index - pre));
int idx = -1;
if (m.Groups.Count >= 3)
{
string aname = "";
MatchCollection x = jn.Matches(m.Value);
if (x.Count > 0 && x[0].Groups.Count > 1)
aname = x[0].Groups[1].ToString();
AType t = AType.VALUE;
if (m.Groups[1].Length > 0) t = AType.NAME;
if (m.Groups[2].Length > 0) t = AType.OPEN;
if (m.Groups[3].Length > 0) t = AType.CLOSE;
if (aname.Length > 0)
{
tokens.Add(aname, t);
idx = tokens.Bag.Count - 1;
}
Indexing(aname, t, idx, iNames, iGroups);
}
pre = m.Index + m.Length;
}
if (pre < content.Length)
tokens.Add(content.Substring(pre, content.Length - pre));
// copy tokens into buffer
buffer = new Atom("", AType.GROUP);
for (int i = 0; i < tokens.Bag.Count; i++)
buffer.Add(tokens.Bag[i].Tag, tokens.Bag[i].Kin);
// initialize index of output names
_iname = new Dictionary<string, int>();
foreach (string k in iNames.Keys)
_iname[k] = iNames[k];
// initialize index of output groups
_igroup = new Dictionary<string, tRange>();
foreach (string k in iGroups.Keys)
{
tRange t = iGroups[k];
_igroup[k] = new tRange(t.head, t.toe);
}
}
public string Get()
{
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < buffer.Bag.Count; i++)
{
switch (buffer.Bag[i].Kin)
{
case AType.VALUE:
sb.Append(buffer.Bag[i].Tag);
break;
case AType.NAME:
sb.Append(buffer.Bag[i].Data);
break;
case AType.OPEN:
case AType.CLOSE:
break;
default: break;
}
}
return sb.ToString();
}
}
public class Filler
{
private Templet t = null;
public int[] di;
public string[] names;
public string Group { get; set; }
public tRange Range { get; set; }
private int seq = 0;
public Filler(Templet tl, string markers = null)
{
t = tl;
if (markers != null)
names = markers.Split(new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries);
else
names = null;
}
public void init(int length)
{
di = new int[length];
for (int i = 0; i < length; i++)
di[i] = -1;
seq = 0;
Group = "";
Range = null;
}
// clear contents inside marked object or group
public void Clear()
{
object[] x = new object[di.Length];
for (int i = 0; i < di.Length; i++)
x[i] = null;
t.Set(di, x);
}
// set value for marked object,
// or add row to group and set value to columns
public void Set(params object[] x)
{
t.Set(di, x);
}
public void Add(params object[] x)
{
if (Group.Length > 0)
{
t.New(Group, seq);
++seq;
t.ReMark(this, Group);
}
t.Set(di, x);
}
}
}
Testing program
Program.cs
Templet m = new Templet(html);
Filler f= m.Mark("title");
f.Set("Phone book");
Filler fcontacts = m.Mark("contacts", "id,name,sex,phone");
fcontacts.Add(1, "Akhmad", "M", "123456");
fcontacts.Add(2, "Barry", "M", "234567");
fcontacts.Add(1, "Charles", "M", "345678");
Console.Out.WriteLine(m.Get());
Still can't do nested loop- yet.
Just use ASP.NET. Whether you use webforms or MVC, it's super easy to have C# in your .cs files, and HTML in your .aspx files.
As with anything in programming, it's 99% up to you to do things right. Flexible UI engines aren't going to enforce that you follow good coding practices.
In principle most any template engine you choose can separate HTML from control logic with the proper architecture. using an MVC (Or MVVM) pattern, if you construct your model in such a way that the controller contains the if/then logic instead of the view you can eliminate it from the view.
That said, the syntax you use is very close to Razor syntax which is easily available for ASP.NET MVC through NuGet packages.
I totally hear you. I built SharpFusion, which has some other stuff in it but if you look for the template.cs file you will see the handler that parses a HTML file and simply replaces out tokens with values that you've made in c#.
Because no XML parsing is done like ASP.NET the framework loads much faster than even an MVC site.
Another alternative is ServiceStack.

Categories

Resources