private static Dictionary<string, PageElement> elementSet = new Dictionary<string, PageElement>();
/// <summary>
/// WikiText -> Elements
/// </summary>
/// <param name="wikitext">< > を中間データとして使用するため、PageElements系以外から呼び出すときはwikitextにHttpUtility.HtmlEncode()の出力を渡すこと。</param>
/// <returns></returns>
public static List<PageElement> Tokenize(PageElement parent, string wikitext, HashSet<Type> targetClasses)
{
//FIXME:wikitextはエスケープ済みでなければならない仕様
// Regex.Replaceの仕様上引数を渡せないので、クロージャ内で使える変数を用意
Type elementClass = null;
PageElement _elementInstance = null;
Match _match = null;
bool matched;
Func<Match, string> createElement = delegate(Match m)
{
matched = true;
string ret;
var classes = new HashSet<Type>(targetClasses);
classes.Remove(elementClass);
if (elementClass.GetMethod("Merge") != null)
{
// 連結可能Element
Debug.Assert(elementClass.Name != null);
if (_elementInstance != null && _elementInstance.GetType().Name == elementClass.Name && _match.Index + _match.Length == m.Index)
{
// 連結、既存Elementに連結
elementClass.GetMethod("Merge").Invoke(null, new object[] { parent, m, classes, _elementInstance });
//_elem = _elem;
ret = "";
}
else
{
var elementInstance = (PageElement)Assembly.GetAssembly(elementClass).CreateInstance(elementClass.FullName, false, BindingFlags.CreateInstance, null, new object[] { parent, m, classes }, null, null);
elementSet.Add(elementInstance.GetType().ToString() + ":" + elementInstance.GetHashCode().ToString(), elementInstance);
_elementInstance = elementInstance;
ret = "<" + elementInstance.GetType().ToString() + ":" + elementInstance.GetHashCode().ToString() + ">";
}
}
else
{
var elementInstance = (PageElement)Assembly.GetAssembly(elementClass).CreateInstance(elementClass.FullName, false, BindingFlags.CreateInstance, null, new object[] { parent, m, classes }, null, null);
elementSet.Add(elementInstance.GetType().ToString() + ":" + elementInstance.GetHashCode().ToString(), elementInstance);
_elementInstance = null;
ret = "<" + elementInstance.GetType().ToString() + ":" + elementInstance.GetHashCode().ToString() + ">";
}
_match = m;
return ret;
};
Func<Match, string> createPlaintextElement = delegate(Match m)
{
matched = true;
// Plains系オブジェクト生成
//var elementInstance = PageElements.Plain.Construct(parent, m.Groups[0].Value, targetPlainClasses);
var elementInstance = PageElements.Plain.Construct(parent, m.Groups[0].Value, targetClasses);
if (elementInstance != null)
{
elementSet.Add(elementInstance.GetType().ToString() + ":" + elementInstance.GetHashCode().ToString(), elementInstance);
return "<" + elementInstance.GetType().ToString() + ":" + elementInstance.GetHashCode().ToString() + ">";
}
else
{
return "";
}
};
var elements = new List<PageElement>();
var plugins = new HashSet<Type>(targetClasses);
do
{
// 置き換えが起きるたびに状況が変わるので、置き換えが起きなくなるまで置き換えを試す
matched = false;
// targetClasses Wikitextを消費しない記法で無限再帰するのを防止
foreach (var plugin in plugins)
{
if (plugin.IsSubclassOf(typeof(PageElements.Notation)))
{
string pattern = (string)plugin.GetProperty("Pattern").GetGetMethod().Invoke(null, null);
if (pattern != null)
{
elementClass = plugin;
//// ignoreClassesでも対象Wikitextが短くなっているなら無限再帰を防げる。
//if (targetClasses.Contains(plugin))
// wikitext = Regex.Replace(wikitext, @"(?<=.)" + pattern + @"|" + pattern + @"(?=.)", new MatchEvaluator(createElement), RegexOptions.Singleline & RegexOptions.IgnoreCase);
//else
// wikitext = Regex.Replace(wikitext, pattern, new MatchEvaluator(createElement), RegexOptions.Singleline & RegexOptions.IgnoreCase);
// matchedはdelegate内で更新
wikitext = Regex.Replace(wikitext, pattern, new MatchEvaluator(createElement), RegexOptions.Singleline & RegexOptions.IgnoreCase);
}
}
}
} while (matched);
Debug.WriteLine("1 ====================\n" + wikitext + "\n====================\n");
{
//TODO:Variables系
}
Debug.WriteLine("2 ====================\n" + wikitext + "\n====================\n");
{
// Plaintext系 この時点で > ... < 間にあるテキスト全てが対象。
//FIXME:名前付きグループを使えば3つの正規表現をまとめられる。
wikitext = Regex.Replace(wikitext, @"^([^<]+)", new MatchEvaluator(createPlaintextElement), RegexOptions.Singleline & RegexOptions.IgnoreCase);
wikitext = Regex.Replace(wikitext, @"(?<=\>)((?:[^<>]|\n)+?)(?=\<)", new MatchEvaluator(createPlaintextElement), RegexOptions.Singleline & RegexOptions.IgnoreCase);
wikitext = Regex.Replace(wikitext, @"([^>]+)$", new MatchEvaluator(createPlaintextElement), RegexOptions.Singleline & RegexOptions.IgnoreCase);
}
Debug.Assert(Regex.Replace(wikitext, @"\<[^\>]+?\>", "", RegexOptions.Singleline & RegexOptions.IgnoreCase) == "");
{
// Elements構築
foreach (Match m in Regex.Matches(wikitext, @"\<([^\>]+?)\>", RegexOptions.Singleline & RegexOptions.IgnoreCase))
{
elements.Add(elementSet[m.Groups[1].Value]);
}
}
return elements;
}