private static Dictionary<string, PageElement> elementSet = new Dictionary<string, PageElement>();
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;
}


