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;

}