using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Windows.Forms;
namespace CodeTemplate
{
/// <summary>
/// Summary description for TemplateFormatter.
/// </summary>
internal class TemplateFormatter
{
static TemplateFormatter()
{
s_keywords = new Hashtable();
s_keywords.Add("SOLUTION", new KeywordProvider(TemplateFormatter.getSolution));
s_keywords.Add("PROJECT", new KeywordProvider(TemplateFormatter.getProject));
s_keywords.Add("FILE", new KeywordProvider(TemplateFormatter.getFile));
s_keywords.Add("NOW", new KeywordProvider(TemplateFormatter.getTime));
s_keywords.Add("TODAY", new KeywordProvider(TemplateFormatter.getDate));
s_keywords.Add("GUID", new KeywordProvider(TemplateFormatter.getGUID));
s_keywords.Add("SELECTION", new KeywordProvider(TemplateFormatter.getSelection));
s_formatters = new Hashtable();
s_formatters.Add("U", new FormatProvider(TemplateFormatter.formatUppercase));
s_formatters.Add("L", new FormatProvider(TemplateFormatter.formatLowercase));
s_formatters.Add("W", new FormatProvider(TemplateFormatter.formatWidth));
s_formatters.Add("R", new FormatProvider(TemplateFormatter.formatReplace));
s_formatters.Add("DRIVE", new FormatProvider(TemplateFormatter.formatDrive));
s_formatters.Add("DIR", new FormatProvider(TemplateFormatter.formatDir));
s_formatters.Add("FNAME", new FormatProvider(TemplateFormatter.formatFName));
s_formatters.Add("EXT", new FormatProvider(TemplateFormatter.formatExt));
s_formatters.Add("PATH", new FormatProvider(TemplateFormatter.formatPath));
s_formatters.Add("BASENAME", new FormatProvider(TemplateFormatter.formatBaseName));
s_formatters.Add("FMT", new FormatProvider(TemplateFormatter.formatFormat));
}
#region Keyword providers
private delegate Object KeywordProvider();
private static Object getSolution()
{
return Connect.Application.Solution.FullName;
}
private static Object getProject()
{
return Connect.Application.ActiveDocument.ProjectItem.ContainingProject.FileName;
}
private static Object getFile()
{
return Connect.Application.ActiveDocument.FullName;
}
private static Object getTime()
{
return DateTime.Now;
}
private static Object getDate()
{
return DateTime.Today;
}
private static Object getGUID()
{
return Guid.NewGuid();
}
private static Object getSelection()
{
EnvDTE.TextSelection text = (EnvDTE.TextSelection) Connect.Application.ActiveDocument.Selection;
if(text.IsEmpty)
return "";
return text.Text;
}
#endregion
#region Format providers
private delegate String FormatProvider(Object obj, String param);
private static void parseStartLength(String inStr, Int32 maxLength, out Int32 start, out Int32 length)
{
String[] values = inStr.Split(',');
start = length = -1;
if(values.Length == 2)
{
start = Int32.Parse(values[0]);
length = Int32.Parse(values[1]);
}
else if(values.Length == 1)
start = Int32.Parse(values[0]);
if(start < 0 || start > maxLength)
start = 0;
if(length < 0 || length > maxLength)
length = maxLength;
if(start + length > maxLength)
length = maxLength - start;
}
private static String formatUppercase(Object obj, String param)
{
String str = (String) obj;
if(param == null)
return str.ToUpper();
Int32 start, length;
parseStartLength((String) param, str.Length, out start, out length);
if(start == 0 && length == str.Length)
return str.ToUpper();
String result = String.Empty;
if(start > 0)
result = str.Substring(0, start);
result += str.Substring(start, length).ToUpper();
if(start + length < str.Length)
result += str.Substring(start + length);
return result;
}
private static String formatLowercase(Object obj, String param)
{
String str = (String) obj;
if(param == null)
return str.ToLower();
Int32 start, length;
parseStartLength((String) param, str.Length, out start, out length);
if(start == 0 && length == str.Length)
return str.ToLower();
String result = String.Empty;
if(start > 0)
result = str.Substring(0, start);
result += str.Substring(start, length).ToLower();
if(start + length < str.Length)
result += str.Substring(start + length);
return result;
}
private static String formatWidth(Object obj, String param)
{
String text = (String) obj;
Int32 len = text.Length;
try
{
len = Int32.Parse(param);
}
catch(Exception ex)
{
throw new ApplicationException(param + " is an invalid number", ex);
}
if(len <= 0 || len >= text.Length)
return text;
return text.Substring(0, len);
}
private static String formatReplace(Object obj, String param)
{
String[] values = param.Split(',');
if(values.Length == 2)
return ((String)obj).Replace(values[0], values[1]);
return ((String) obj).Replace(param[0], param[1]);
}
private static String formatDrive(Object obj, String param)
{
return ((String) obj).Substring(0, 2);
}
private static String formatDir(Object obj, String param)
{
return Path.GetDirectoryName((String) obj).Substring(2);
}
private static String formatFName(Object obj, String param)
{
return Path.GetFileNameWithoutExtension((String) obj);
}
private static String formatExt(Object obj, String param)
{
return Path.GetExtension((String) obj);
}
private static String formatPath(Object obj, String param)
{
return Path.GetDirectoryName((String) obj);
}
private static String formatBaseName(Object obj, String param)
{
return Path.GetFileName((String) obj);
}
private static String formatFormat(Object obj, String param)
{
String fmt = "{0:" + param + "}";
return String.Format(fmt, obj);
}
#endregion
#region Template TemplateFormatter
private static Object getTagValue(Tag tag)
{
if(tag.IsPrompt)
return null;
KeywordProvider prov = (KeywordProvider)s_keywords[tag.Name];
return (prov != null) ? prov() : Environment.GetEnvironmentVariable(tag.Name);
}
private static String applyTagFormat(Tag tag)
{
// If the "TODAY" tag doesn't have a "FMT" format attached, then assume long date ("D")
if(tag.Name == "TODAY" && tag.Formats["FMT"] == null)
tag.Formats.Add("FMT", "D");
String newVal = null;
// If a tag has the "FMT" format, it's formatter has to be called first
Format fmt = (Format) tag.Formats["FMT"];
if(fmt != null)
newVal = ((FormatProvider)s_formatters[fmt.Name])(tag.Value, fmt.Arguments);
else if(tag.Value != null)
newVal = tag.Value.ToString();
foreach(Format f in tag.Formats)
{
if(f.Name == "FMT")
continue;
FormatProvider prov = (FormatProvider)s_formatters[f.Name];
if(prov != null)
newVal = prov(newVal, f.Arguments);
}
return newVal;
}
private static String processTag(Tag tag)
{
tag.Value = getTagValue(tag);
return applyTagFormat(tag);
}
public static void ReplaceTag(ref StringBuilder templateBuf, Tag tag)
{
templateBuf.Replace(tag.RawTag, applyTagFormat(tag));
}
public static void ReplaceTags(ref StringBuilder templateBuf, IEnumerable tags)
{
foreach(Tag tag in tags)
{
if(tag.Value == null)
tag.Value = String.Empty;
ReplaceTag(ref templateBuf, tag);
}
}
private static ArrayList GetTagsFromText(String templateText)
{
Regex r = new Regex(@"<%\s*(?<1>.*?)%>", RegexOptions.IgnoreCase);
ArrayList tags = new ArrayList();
for(Match m = r.Match(templateText); m.Success; m = m.NextMatch())
tags.Add(new Tag(m.Groups[1].ToString(), m.Value));
return tags;
}
private static void Parse(String templateText, TemplateFile tplFile, ref ArrayList prompts, ref StringBuilder sb)
{
ArrayList tags = GetTagsFromText(templateText);
foreach(Tag tag in tags)
{
if(tag.IsPrompt)
{
prompts.Add(tag);
continue;
}
else if(tag.IsNestedTemplate)
{
TemplateMenuItem mi = tplFile.GetTemplateByID(tag.Formats[0].Name);
if(mi != null)
{
sb.Replace(tag.RawTag, mi.TemplateText);
Parse(mi.TemplateText, tplFile, ref prompts, ref sb);
continue;
}
}
sb.Replace(tag.RawTag, processTag(tag));
}
}
public static String Format(String caption, String templateText, TemplateFile tplFile, IWin32Window parentWindow)
{
ArrayList prompts = new ArrayList();
StringBuilder sb = new StringBuilder(templateText);
Parse(templateText, tplFile, ref prompts, ref sb);
if(prompts.Count > 0)
{
ParamForm form = new ParamForm(caption, sb.ToString());
foreach(Tag tag in prompts)
form.AddField(tag);
if(form.ShowDialog(parentWindow) == DialogResult.Cancel)
return null;
return form.Text;
}
return Delim.Unescape(sb);
}
#endregion
private static Hashtable s_keywords;
private static Hashtable s_formatters;
}
}