PageRenderTime 161ms CodeModel.GetById 27ms app.highlight 121ms RepoModel.GetById 1ms app.codeStats 1ms

/cwxeditor_src/cwx/flag.d

https://bitbucket.org/k4nagatsuki/cwxeditor
D | 1693 lines | 1451 code | 55 blank | 187 comment | 262 complexity | 08d6acc656f8589a2dce200a27af8a0d MD5 | raw file
   1
   2module cwx.flag;
   3
   4import cwx.utils;
   5import cwx.xml;
   6import cwx.path;
   7import cwx.usecounter;
   8import cwx.system;
   9
  10import std.algorithm;
  11import std.array;
  12import std.datetime;
  13import std.string;
  14import std.typecons;
  15import std.exception;
  16import std.conv;
  17
  18private static const {
  19	string XML_ROOT_FLAGS_AND_STEPS = "FlagsAndSteps";
  20	string XML_ROOT_FLAG_DIRECTORY = "FlagDirectory";
  21	string XML_ATT_ROOT_ID = "rootId";
  22	string XML_ATT_PATH = "path";
  23	string XML_ATT_ROOT_NAME = "rootName";
  24}
  25
  26/// ?????????
  27public class FlagException : Exception {
  28public:
  29	this (string msg) { mixin(S_TRACE);
  30		super(msg);
  31	}
  32}
  33
  34/// ????????????XML??????
  35public string getXML(FlagDir parent, Flag[] flags, Step[] steps) { mixin(S_TRACE);
  36	auto e = XNode.create(XML_ROOT_FLAGS_AND_STEPS);
  37	auto _root = parent.root;
  38	e.newAttr(XML_ATT_PATH, parent.path);
  39	e.newAttr( XML_ATT_ROOT_ID, _root.id);
  40	auto fe = e.newElement("Flags");
  41	foreach (flag; flags) { mixin(S_TRACE);
  42		assert (flag.parent.root == _root);
  43		assert (flag.parent == parent);
  44		flag.toNode(fe);
  45	}
  46	auto se = e.newElement("Steps");
  47	foreach (step; steps) { mixin(S_TRACE);
  48		assert (step.parent.root == _root);
  49		assert (step.parent == parent);
  50		step.toNode(se);
  51	}
  52	return e.text;
  53}
  54
  55/// ???????????????????XML??????
  56public string getXML(string rootName, FlagDir dir) { mixin(S_TRACE);
  57	return toNode(rootName, dir).text;
  58}
  59/// ditto
  60XNode toNode(string rootName, FlagDir dir) { mixin(S_TRACE);
  61	auto ret = XNode.create(XML_ROOT_FLAG_DIRECTORY);
  62	toNode(ret, dir);
  63	auto _root = dir.root;
  64	ret.newAttr(XML_ATT_ROOT_ID, _root.id);
  65	if (_root == dir) { mixin(S_TRACE);
  66		ret.newAttr(XML_ATT_ROOT_NAME, rootName);
  67	}
  68	return ret;
  69}
  70private void toNode(ref XNode ret, FlagDir dir) { mixin(S_TRACE);
  71	ret.newAttr(XML_ATT_PATH, dir.path);
  72	auto fe = ret.newElement("Flags");
  73	foreach (flag; dir.flags) { mixin(S_TRACE);
  74		flag.toNode(fe);
  75	}
  76	auto se = ret.newElement("Steps");
  77	foreach (step; dir.steps) { mixin(S_TRACE);
  78		step.toNode(se);
  79	}
  80	foreach (subdir; dir.subDirs) { mixin(S_TRACE);
  81		auto e = ret.newElement(XML_ROOT_FLAG_DIRECTORY);
  82		toNode(e, subdir);
  83	}
  84}
  85
  86/// ????
  87public class Flag : CWXPath {
  88private:
  89	string _name;
  90	string _on;
  91	string _off;
  92	bool _onOff;
  93	FlagDir _parent;
  94	void delegate() _change = null;
  95public:
  96	/// ???ID??????
  97	alias toFlagId toID;
  98
  99	/// ???????????
 100	this (Flag copyBase) { mixin(S_TRACE);
 101		_name = copyBase.name;
 102		_on = copyBase.on;
 103		_off = copyBase.off;
 104		_onOff = copyBase.onOff;
 105	}
 106	/// ???On/Off???????On/Off?????????????????
 107	this (string name, string on, string off, bool onOff) { mixin(S_TRACE);
 108		_on = on;
 109		_off = off;
 110		_onOff = onOff;
 111		_name = FlagDir.validName(name);
 112	}
 113	/// flag?????????????
 114	void copyFrom(Flag flag) { mixin(S_TRACE);
 115		name = flag.name;
 116		on = flag.on;
 117		off = flag.off;
 118		onOff = flag.onOff;
 119	}
 120	/// ??????????????
 121	@property
 122	FlagDir parent() { mixin(S_TRACE);
 123		return _parent;
 124	}
 125	/// ditto
 126	@property
 127	const
 128	const(FlagDir) parent() { mixin(S_TRACE);
 129		return _parent;
 130	}
 131	/// ditto
 132	@property
 133	private void parent(FlagDir parent) { mixin(S_TRACE);
 134		assert (!parent || !parent.getFlag(name));
 135		_parent = parent;
 136	}
 137	/// ???????????
 138	@property
 139	FlagDir root() { mixin(S_TRACE);
 140		return _parent.root;
 141	}
 142	/// ditto
 143	@property
 144	const
 145	const(FlagDir) root() { mixin(S_TRACE);
 146		return _parent.root;
 147	}
 148	/// ????????????
 149	@property
 150	void changeHandler(void delegate() change) { mixin(S_TRACE);
 151		_change = change;
 152	}
 153	/// ???????????????????????
 154	@property
 155	const
 156	string name() { mixin(S_TRACE);
 157		return _name;
 158	}
 159	/// ditto
 160	@property
 161	bool name(string name) { mixin(S_TRACE);
 162		name = FlagDir.validName(name);
 163		if (!_parent || _parent.canAppend!Flag(name)) { mixin(S_TRACE);
 164			if (_change && _name != name) _change();
 165			_name = name;
 166			return true;
 167		}
 168		return false;
 169	}
 170	/// On???????
 171	@property
 172	const
 173	string on() { mixin(S_TRACE);
 174		return _on;
 175	}
 176	/// ditto
 177	@property
 178	void on(string on) { mixin(S_TRACE);
 179		if (_change && _on != on) _change();
 180		_on = on;
 181	}
 182	/// Off???????
 183	@property
 184	const
 185	string off() { mixin(S_TRACE);
 186		return _off;
 187	}
 188	/// ditto
 189	@property
 190	void off(string off) { mixin(S_TRACE);
 191		if (_change && _off != off) _change();
 192		_off = off;
 193	}
 194	/// On/Off?????
 195	@property
 196	const
 197	bool onOff() { mixin(S_TRACE);
 198		return _onOff;
 199	}
 200	/// ditto
 201	@property
 202	void onOff(bool onOff) { mixin(S_TRACE);
 203		if (_change && _onOff != onOff) _change();
 204		_onOff = onOff;
 205	}
 206	const
 207	override int opCmp(Object o) { mixin(S_TRACE);
 208		return icmp(name, (cast(Flag) o).name);
 209	}
 210	/// ??????????????
 211	@property
 212	const
 213	string path() { mixin(S_TRACE);
 214		return _parent.path ~ _name;
 215	}
 216
 217	/// ??????XML????????
 218	const
 219	string toXml() { mixin(S_TRACE);
 220		auto doc = XNode.create(XML_ROOT_FLAG_DIRECTORY);
 221		toNode(doc);
 222		return doc.text;
 223	}
 224	/// XML???????????????
 225	static Flag createFromNode(ref XNode fe, in XMLInfo ver) { mixin(S_TRACE);
 226		string name = null;
 227		string tv = "TRUE";
 228		string fv = "FALSE";
 229		bool def = parseBool(fe.attr("default", true));
 230		fe.onTag["Name"] = (ref XNode n) {name = FlagDir.basename(n.value);};
 231		fe.onTag["True"] = (ref XNode n) {tv = n.value;};
 232		fe.onTag["False"] = (ref XNode n) {fv = n.value;};
 233		fe.parse();
 234		if (!name) throw new FlagException("Flag name not found.");
 235		return new Flag(name, tv, fv, def);
 236	}
 237	/// XML???????????????????
 238	const
 239	void toNode(ref XNode node) { mixin(S_TRACE);
 240		auto e = node.newElement("Flag");
 241		e.newAttr("default", fromBool(_onOff));
 242		e.newElement("Name", path);
 243		e.newElement("True", on);
 244		e.newElement("False", off);
 245	}
 246	@property
 247	override string cwxPath(bool id) { mixin(S_TRACE);
 248		return cpjoin(_parent, "flag", .cCountUntil!("a is b")(_parent.flags, this), id);
 249	}
 250	override CWXPath findCWXPath(string path) { mixin(S_TRACE);
 251		if (cpempty(path)) return this;
 252		return null;
 253	}
 254	@property
 255	const
 256	override const(CWXPath)[] cwxChilds() {return [];}
 257	@property
 258	CWXPath cwxParent() {return _parent;}
 259}
 260
 261/// ?????
 262public class Step : CWXPath {
 263private:
 264	string _name;
 265	string[] _vals;
 266	uint _select;
 267	FlagDir _parent;
 268	void delegate() _change = null;
 269public:
 270	/// ???ID??????
 271	alias toStepId toID;
 272
 273	/// ???????????
 274	this (Step copyBase) { mixin(S_TRACE);
 275		_name = copyBase.name;
 276		_vals = copyBase._vals.dup;
 277		_select = copyBase._select;
 278	}
 279	/// ???????????????????????????????????
 280	this (string name, string[] vals, uint select) { mixin(S_TRACE);
 281		_vals = vals;
 282		_select = select;
 283		_name = FlagDir.validName(name);
 284	}
 285	/// step?????????????
 286	void copyFrom(Step step) { mixin(S_TRACE);
 287		name = step.name;
 288		setValues(step.values, step.select);
 289	}
 290	/// ???????????????
 291	@property
 292	FlagDir parent() { mixin(S_TRACE);
 293		return _parent;
 294	}
 295	/// ditto
 296	@property
 297	const
 298	const(FlagDir) parent() { mixin(S_TRACE);
 299		return _parent;
 300	}
 301	/// ditto
 302	@property
 303	private void parent(FlagDir parent) { mixin(S_TRACE);
 304		assert (!parent || !parent.getStep(name));
 305		_parent = parent;
 306	}
 307	/// ???????????
 308	@property
 309	FlagDir root() { mixin(S_TRACE);
 310		return _parent.root;
 311	}
 312	/// ditto
 313	@property
 314	const
 315	const(FlagDir) root() { mixin(S_TRACE);
 316		return _parent.root;
 317	}
 318	/// ????????????
 319	@property
 320	void changeHandler(void delegate() change) { mixin(S_TRACE);
 321		_change = change;
 322	}
 323	/// ??????
 324	@property
 325	const
 326	string name() { mixin(S_TRACE);
 327		return _name;
 328	}
 329	/// ditto
 330	@property
 331	bool name(string name) { mixin(S_TRACE);
 332		name = FlagDir.validName(name);
 333		if (!_parent || _parent.canAppend!Step(name)) { mixin(S_TRACE);
 334			if (_change && _name != name) _change();
 335			_name = name;
 336			return true;
 337		}
 338		return false;
 339	}
 340
 341	/// ????????????????
 342	void setValue(uint index, string value) { mixin(S_TRACE);
 343		if (_change && _vals[index] != value) _change();
 344		_vals[index] = value;
 345	}
 346	/// ??????????????
 347	const
 348	string getValue(uint index) { mixin(S_TRACE);
 349		return _vals[index];
 350	}
 351
 352	/// ????????????
 353	@property
 354	const
 355	uint count() { mixin(S_TRACE);
 356		return _vals.length;
 357	}
 358
 359	/// ?????????????
 360	@property
 361	const
 362	uint select() { mixin(S_TRACE);
 363		return _select;
 364	}
 365	/// ditto
 366	@property
 367	void select(uint select) { mixin(S_TRACE);
 368		if (_change && _select != select) _change();
 369		_select = .min(select, _vals.length - 1);
 370	}
 371
 372	/// ??????????????
 373	@property
 374	const
 375	string value() { mixin(S_TRACE);
 376		return _vals[_select];
 377	}
 378	/// ??????????
 379	@property
 380	string[] values() { mixin(S_TRACE);
 381		return _vals;
 382	}
 383	@property
 384	const
 385	const(string)[] values() { mixin(S_TRACE);
 386		return _vals;
 387	}
 388	/// ?????????????????
 389	void setValues(string[] vals, int select) { mixin(S_TRACE);
 390		assert (select < vals.length);
 391		if (_change && (_vals != vals || _select != select)) _change();
 392		_vals = vals;
 393		_select = select;
 394	}
 395
 396	const
 397	override int opCmp(Object o) { mixin(S_TRACE);
 398		return icmp(name, (cast(Step) o).name);
 399	}
 400
 401	/// ??????????
 402	@property
 403	const
 404	string path() { mixin(S_TRACE);
 405		return _parent.path ~ _name;
 406	}
 407
 408	/// ???????XML????????
 409	const
 410	string toXml() { mixin(S_TRACE);
 411		auto doc = XNode.create(XML_ROOT_FLAG_DIRECTORY);
 412		toNode(doc);
 413		return doc.text;
 414	}
 415	/// XML???????????????
 416	static Step createFromNode(ref XNode se, in XMLInfo ver) { mixin(S_TRACE);
 417		string[] vals;
 418		string name = null;
 419		int def = se.attr!(int)("default", true);
 420		se.onTag["Name"] = (ref XNode n) {name = FlagDir.basename(n.value);};
 421		se.onTag[null] = (ref XNode n) { mixin(S_TRACE);
 422			if (startsWith(n.name, "Value")) { mixin(S_TRACE);
 423				auto numStr = n.name[5 .. $];
 424				if (isNumeric(numStr)) { mixin(S_TRACE);
 425					int num = to!(int)(numStr);
 426					if (num < 0) return;
 427					if (vals.length <= num) vals.length = num + 1;
 428					vals[num] = n.value;
 429				}
 430			}
 431		};
 432		se.parse();
 433		if (!name) throw new FlagException("Step name not found.");
 434		if (def < 0 || vals.length <= def) { mixin(S_TRACE);
 435			throw new FlagException("Step default value invalid. Count: " ~ to!(string)(vals.length) ~ ", default: " ~ to!(string)(def));
 436		}
 437		return new Step(name, vals, def);
 438	}
 439	/// ?????XML????????????????????
 440	const
 441	void toNode(ref XNode node) { mixin(S_TRACE);
 442		auto e = node.newElement("Step");
 443		e.newAttr("default", _select);
 444		e.newElement("Name", path);
 445		for (int i = 0; i < _vals.length; i++) { mixin(S_TRACE);
 446			e.newElement("Value" ~ to!(string)(i), _vals[i]);
 447		}
 448	}
 449	@property
 450	override string cwxPath(bool id) { mixin(S_TRACE);
 451		return cpjoin(_parent, "step", .cCountUntil!("a is b")(_parent.steps, this), id);
 452	}
 453	override CWXPath findCWXPath(string path) { mixin(S_TRACE);
 454		if (cpempty(path)) return this;
 455		return null;
 456	}
 457	@property
 458	const
 459	override const(CWXPath)[] cwxChilds() {return [];}
 460	@property
 461	CWXPath cwxParent() {return _parent;}
 462}
 463
 464/// ???/???????????????????????????
 465public class FlagDir : CWXPath {
 466private:
 467	string _name = "";
 468	CWXPath _owner = null;
 469	FlagDir _parent = null;
 470	FlagDir[] _subdir;
 471	Flag[] _flags;
 472	Step[] _steps;
 473	string _id;
 474	int delegate(string, string) _sorter = null;
 475	void delegate() _change = null;
 476public:
 477	/// ????????
 478	static immutable string SEPARATOR = "\\";
 479	/// ditto
 480	static immutable string SEPARATOR_REGEX = "\\\\";
 481	/// ????????
 482	static string join(string parent, string path) { mixin(S_TRACE);
 483		if (!parent.length) { mixin(S_TRACE);
 484			return path;
 485		}
 486		if (parent.endsWith(SEPARATOR.dup)) { mixin(S_TRACE);
 487			return parent ~ path;
 488		}
 489		return parent ~ SEPARATOR ~ path;
 490	}
 491	/// ???????????????
 492	package this (CWXPath owner) { mixin(S_TRACE);
 493		_id = format("%08X", &this) ~ "-" ~ to!(string)(Clock.currTime());
 494		_owner = owner;
 495	}
 496	/// ??????????????
 497	/// Params:
 498	/// name = ????????
 499	this (string name) { mixin(S_TRACE);
 500		_id = format("%08X", &this) ~ "-" ~ to!(string)(Clock.currTime());
 501		_name = validName(name);
 502	}
 503	/// ???????????
 504	/// ???????????????????
 505	this (FlagDir copyBase) { mixin(S_TRACE);
 506		name = copyBase.name;
 507		foreach (d; copyBase.subDirs) { mixin(S_TRACE);
 508			add(new FlagDir(d));
 509		}
 510		foreach (f; copyBase.flags) { mixin(S_TRACE);
 511			add(new Flag(f));
 512		}
 513		foreach (s; copyBase.steps) { mixin(S_TRACE);
 514			add(new Step(s));
 515		}
 516	}
 517	@property
 518	override string cwxPath(bool id) { mixin(S_TRACE);
 519		if (_owner) { mixin(S_TRACE);
 520			return cpjoin(_owner, "variable", id);
 521		} else if (_parent) { mixin(S_TRACE);
 522			return cpjoin(_parent, "dir", .cCountUntil!("a is b")(_parent.subDirs, this), id);
 523		}
 524		return "";
 525	}
 526	override CWXPath findCWXPath(string path) { mixin(S_TRACE);
 527		if (cpempty(path)) return this;
 528		auto cate = cpcategory(path);
 529		switch (cate) {
 530		case "flag": { mixin(S_TRACE);
 531			auto index = cpindex(path);
 532			if (index >= flags.length) return null;
 533			return flags[index].findCWXPath(cpbottom(path));
 534		}
 535		case "step": { mixin(S_TRACE);
 536			auto index = cpindex(path);
 537			if (index >= steps.length) return null;
 538			return steps[index].findCWXPath(cpbottom(path));
 539		}
 540		case "dir": { mixin(S_TRACE);
 541			auto index = cpindex(path);
 542			if (index >= subDirs.length) return null;
 543			return subDirs[index].findCWXPath(cpbottom(path));
 544		}
 545		default: break;
 546		}
 547		return null;
 548	}
 549	@property
 550	const
 551	override const(CWXPath)[] cwxChilds() { mixin(S_TRACE);
 552		const(CWXPath)[] r;
 553		foreach (a; _flags) r ~= a;
 554		foreach (a; _steps) r ~= a;
 555		foreach (a; _subdir) r ~= a;
 556		return r;
 557	}
 558	@property
 559	CWXPath cwxParent() {return _owner ? _owner : _parent;}
 560
 561	/// ?????????????????????????
 562	/// ????????????????????
 563	@property
 564	void sorter(int delegate(string, string) sorter) { mixin(S_TRACE);
 565		if (parent) throw new Exception("FlagDir sorter");
 566		sorterImpl(sorter);
 567	}
 568	@property
 569	private void sorterImpl(int delegate(string, string) sorter) { mixin(S_TRACE);
 570		_sorter = sorter;
 571		foreach (sub; subDirs) { mixin(S_TRACE);
 572			sub.sorterImpl(sorter);
 573		}
 574	}
 575	/// ????????
 576	@property
 577	FlagDir parent() { mixin(S_TRACE);
 578		return _parent;
 579	}
 580	/// ditto
 581	@property
 582	const
 583	const(FlagDir) parent() { mixin(S_TRACE);
 584		return _parent;
 585	}
 586	/// ditto
 587	@property
 588	private void parent(FlagDir parent) { mixin(S_TRACE);
 589		assert (!parent || !parent.getSubDir(name));
 590		_parent = parent;
 591	}
 592	/// ????????????
 593	@property
 594	void changeHandler(void delegate() change) { mixin(S_TRACE);
 595		foreach (f; _flags) { mixin(S_TRACE);
 596			f.changeHandler = change;
 597		}
 598		foreach (s; _steps) { mixin(S_TRACE);
 599			s.changeHandler = change;
 600		}
 601		foreach (s; _subdir) { mixin(S_TRACE);
 602			s.changeHandler = change;
 603		}
 604		_change = change;
 605	}
 606
 607	/// ???????????
 608	@property
 609	FlagDir root() { mixin(S_TRACE);
 610		auto dir = this;
 611		while (dir.parent !is null) { mixin(S_TRACE);
 612			dir = dir.parent;
 613		}
 614		return dir;
 615	}
 616	/// ditto
 617	@property
 618	const
 619	const(FlagDir) root() { mixin(S_TRACE);
 620		Rebindable!(const(FlagDir)) dir = this;
 621		while (dir.parent !is null) { mixin(S_TRACE);
 622			dir = dir.parent;
 623		}
 624		return dir;
 625	}
 626
 627	/// ????????ID?????&???????????
 628	@property
 629	const
 630	string id() { mixin(S_TRACE);
 631		return _id;
 632	}
 633
 634	/// ????????
 635	@property
 636	const
 637	string name() { mixin(S_TRACE);
 638		return _name;
 639	}
 640	/// ditto
 641	@property
 642	bool name(string name) { mixin(S_TRACE);
 643		name = FlagDir.validName(name);
 644		if (!_parent || _parent.canAppendSub(name)) { mixin(S_TRACE);
 645			if (_change && _name != name) _change();
 646			_name = name;
 647			return true;
 648		}
 649		return false;
 650	}
 651
 652	/// ??????????????????????????
 653	/// ????????true????
 654	const
 655	private bool canAppend(F)(string name) { mixin(S_TRACE);
 656		static if (is(F:Flag)) {
 657			name = validName(name);
 658			if (name.length == 0) { mixin(S_TRACE);
 659				return false;
 660			}
 661			foreach (flag; _flags) { mixin(S_TRACE);
 662				if (icmp(flag.name, name) == 0) { mixin(S_TRACE);
 663					return false;
 664				}
 665			}
 666			return true;
 667		} else static if (is(F:Step)) {
 668			name = validName(name);
 669			if (name.length == 0) { mixin(S_TRACE);
 670				return false;
 671			}
 672			foreach (step; _steps) { mixin(S_TRACE);
 673				if (icmp(step.name, name) == 0) { mixin(S_TRACE);
 674					return false;
 675				}
 676			}
 677			return true;
 678		} else { mixin(S_TRACE);
 679			static assert (is(F:FlagDir));
 680			return canAppendSub(name);
 681		}
 682	}
 683	const
 684	private bool canAppendFlag(in Flag f) { mixin(S_TRACE);
 685		return canAppend!Flag(f.name);
 686	}
 687	/// ditto
 688	const
 689	private bool canAppendStep(in Step f) { mixin(S_TRACE);
 690		return canAppend!Step(f.name);
 691	}
 692	/// ditto
 693	const
 694	bool canAppendSub(string name) { mixin(S_TRACE);
 695		name = validName(name);
 696		if (name.length == 0) { mixin(S_TRACE);
 697			return false;
 698		}
 699		foreach (dir; _subdir) { mixin(S_TRACE);
 700			if (icmp(dir.name, name) == 0) { mixin(S_TRACE);
 701				return false;
 702			}
 703		}
 704		return true;
 705	}
 706	/// ????????????????????true????
 707	/// ?????????????????????????????????????
 708	const
 709	bool canAppendSub2(in FlagDir ndir) { mixin(S_TRACE);
 710		if (this == ndir.parent) { mixin(S_TRACE);
 711			return true;
 712		}
 713		Rebindable!(const(FlagDir)) p = this;
 714		do { mixin(S_TRACE);
 715			if (p.get == ndir) { mixin(S_TRACE);
 716				// ????????????????????????????????????
 717				return false;
 718			}
 719			p = p.parent;
 720		} while (p.get !is null);
 721		return canAppendSub(ndir.name);
 722	}
 723
 724	private bool __add(F)(ref F[] arr, F item, bool delegate(in F) canAppend) { mixin(S_TRACE);
 725		if (canAppend(item) || item.parent is this) { mixin(S_TRACE);
 726			if (item.parent is this && arr[$ - 1] is item) return true;
 727			if (item.parent !is null) { mixin(S_TRACE);
 728				item.parent.remove(item);
 729			}
 730			item.parent = this;
 731			arr ~= item;
 732			item.changeHandler = _change;
 733			if (_change) _change();
 734			return true;
 735		}
 736		return false;
 737	}
 738	/// ???????????????????????
 739	bool add(Flag flag) { mixin(S_TRACE);
 740		return __add!(Flag)(_flags, flag, &canAppendFlag);
 741	}
 742	/// ditto
 743	bool add(Step step) { mixin(S_TRACE);
 744		return __add!(Step)(_steps, step, &canAppendStep);
 745	}
 746	/// ditto
 747	bool add(FlagDir sub) { mixin(S_TRACE);
 748		if (__add!(FlagDir)(_subdir, sub, &canAppendSub2)) { mixin(S_TRACE);
 749			sub.sorterImpl = _sorter;
 750			return true;
 751		}
 752		return false;
 753	}
 754	/// ditto
 755	bool insert(int index, FlagDir sub) { mixin(S_TRACE);
 756		if (_subdir.length == index) return add(sub);
 757		if (canAppendSub2(sub) || sub.parent is this) { mixin(S_TRACE);
 758			if (sub.parent is this && _subdir[index] is sub) return true;
 759			if (sub.parent !is null) { mixin(S_TRACE);
 760				sub.parent.remove(sub);
 761			}
 762			sub.parent = this;
 763			_subdir = _subdir[0 .. index] ~ sub ~ _subdir[index .. $];
 764			sub.changeHandler = _change;
 765			if (_change) _change();
 766			return true;
 767		}
 768		return false;
 769	}
 770	private bool __remove(T)(ref T[] arr, T e) { mixin(S_TRACE);
 771		for (int i = 0; i < arr.length; i++) { mixin(S_TRACE);
 772			if (icmp(e.name, arr[i].name) == 0) { mixin(S_TRACE);
 773				e.parent = null;
 774				arr[i].changeHandler = null;
 775				arr = arr[0 .. i] ~ arr[i + 1 .. $];
 776				if (_change) _change();
 777				return true;
 778			}
 779		}
 780		return false;
 781	}
 782	/// ???????????????????????
 783	void remove(Flag flag) { mixin(S_TRACE);
 784		__remove(_flags, flag);
 785	}
 786	/// ditto
 787	void remove(Step step) { mixin(S_TRACE);
 788		__remove(_steps, step);
 789	}
 790	/// ditto
 791	void remove(FlagDir dir) { mixin(S_TRACE);
 792		if (__remove(_subdir, dir)) { mixin(S_TRACE);
 793			dir.sorterImpl = null;
 794		}
 795	}
 796	void removeAll() { mixin(S_TRACE);
 797		foreach (e; _flags) {
 798			e.parent = null;
 799			e.changeHandler = null;
 800			if (_change) _change();
 801		}
 802		_flags = [];
 803		foreach (e; _steps) {
 804			e.parent = null;
 805			e.changeHandler = null;
 806			if (_change) _change();
 807		}
 808		_steps = [];
 809		foreach (e; _subdir) {
 810			e.parent = null;
 811			e.changeHandler = null;
 812			if (_change) _change();
 813		}
 814		_subdir = [];
 815	}
 816
 817	/// ??????????
 818	@property
 819	FlagDir[] subDirs() { mixin(S_TRACE);
 820		return _subdir;
 821	}
 822	/// ?????
 823	@property
 824	Flag[] flags() { mixin(S_TRACE);
 825		return _flags;
 826	}
 827	/// ??????
 828	@property
 829	Step[] steps() { mixin(S_TRACE);
 830		return _steps;
 831	}
 832	/// ???????????????????????????????true?
 833	const
 834	bool containsFlag(string name) { mixin(S_TRACE);
 835		return getFlag(name) !is null;
 836	}
 837	/// ditto
 838	const
 839	bool containsStep(string name) { mixin(S_TRACE);
 840		return getStep(name) !is null;
 841	}
 842	/// ditto
 843	const
 844	bool containsSubDir(string name) { mixin(S_TRACE);
 845		return getStep(name) !is null;
 846	}
 847	/// ?????????????????index????
 848	/// ????????-1????
 849	const
 850	int indexOf(string name) { mixin(S_TRACE);
 851		return .cCountUntil!("0 == icmp(a.name, b)")(_subdir, name);
 852	}
 853
 854	/// ?????????????????
 855	void swapDir(int index1, int index2) { mixin(S_TRACE);
 856		if (index1 == index2) return;
 857		enforce(0 <= index1 && index1 < _subdir.length);
 858		enforce(0 <= index2 && index2 < _subdir.length);
 859		if (_change) _change();
 860		std.algorithm.swap(_subdir[index1], _subdir[index2]);
 861	}
 862
 863	private static F __get(F)(F[] arr, string name) { mixin(S_TRACE);
 864		foreach (f; arr) { mixin(S_TRACE);
 865			if (icmp(f.name, name) == 0) { mixin(S_TRACE);
 866				return f;
 867			}
 868		}
 869		return null;
 870	}
 871	/// ??????????????????????????????
 872	/// ????????null????
 873	Flag getFlag(string name) { mixin(S_TRACE);
 874		return __get!(Flag)(_flags, name);
 875	}
 876	/// ditto
 877	const
 878	const(Flag) getFlag(string name) { mixin(S_TRACE);
 879		return __get!(const Flag)(_flags, name);
 880	}
 881	/// ditto
 882	Step getStep(string name) { mixin(S_TRACE);
 883		return __get!(Step)(_steps, name);
 884	}
 885	/// ditto
 886	const
 887	const(Step) getStep(string name) { mixin(S_TRACE);
 888		return __get!(const Step)(_steps, name);
 889	}
 890	/// ditto
 891	FlagDir getSubDir(string name) { mixin(S_TRACE);
 892		return __get!(FlagDir)(_subdir, name);
 893	}
 894	/// ditto
 895	const
 896	const(FlagDir) getSubDir(string name) { mixin(S_TRACE);
 897		return __get!(const FlagDir)(_subdir, name);
 898	}
 899
 900	/// ??????????????????????
 901	/// ????????????????
 902	@property
 903	Flag[] allFlags() { mixin(S_TRACE);
 904		Flag[] r;
 905		foreach (flg; _flags) { mixin(S_TRACE);
 906			r ~= flg;
 907		}
 908		foreach (dir; _subdir) { mixin(S_TRACE);
 909			r ~= dir.allFlags;
 910		}
 911		return r;
 912	}
 913	/// ditto
 914	@property
 915	const
 916	const(Flag)[] allFlags() { mixin(S_TRACE);
 917		const(Flag)[] r;
 918		foreach (flg; _flags) { mixin(S_TRACE);
 919			r ~= flg;
 920		}
 921		foreach (dir; _subdir) { mixin(S_TRACE);
 922			r ~= dir.allFlags;
 923		}
 924		return r;
 925	}
 926	/// ditto
 927	@property
 928	Step[] allSteps() { mixin(S_TRACE);
 929		Step[] r;
 930		foreach (step; _steps) { mixin(S_TRACE);
 931			r ~= step;
 932		}
 933		foreach (dir; _subdir) { mixin(S_TRACE);
 934			r ~= dir.allSteps;
 935		}
 936		return r;
 937	}
 938	/// ditto
 939	@property
 940	const
 941	const(Step)[] allSteps() { mixin(S_TRACE);
 942		const(Step)[] r;
 943		foreach (step; _steps) { mixin(S_TRACE);
 944			r ~= step;
 945		}
 946		foreach (dir; _subdir) { mixin(S_TRACE);
 947			r ~= dir.allSteps;
 948		}
 949		return r;
 950	}
 951
 952	/// ????????????????true?
 953	@property
 954	const
 955	bool hasFlag() { mixin(S_TRACE);
 956		if (_flags.length) return true;
 957		foreach (dir; _subdir) { mixin(S_TRACE);
 958			if (dir.hasFlag) return true;
 959		}
 960		return false;
 961	}
 962	/// ditto
 963	@property
 964	const
 965	bool hasStep() { mixin(S_TRACE);
 966		if (_steps.length) return true;
 967		foreach (dir; _subdir) { mixin(S_TRACE);
 968			if (dir.hasStep) return true;
 969		}
 970		return false;
 971	}
 972
 973	/// ???????????????????
 974	void sortSubDirs(bool sub = false) { mixin(S_TRACE);
 975		bool cmps(in FlagDir a, in FlagDir b) { mixin(S_TRACE);
 976			if (_sorter) { mixin(S_TRACE);
 977				return _sorter(a.name, b.name) < 0;
 978			} else { mixin(S_TRACE);
 979				return cmp(a.name, b.name) < 0;
 980			}
 981		}
 982		if (!isSortedDlg!(FlagDir)(_subdir, &cmps)) { mixin(S_TRACE);
 983			if (_change) _change();
 984			_subdir = sortDlg!(FlagDir)(_subdir, &cmps);
 985		}
 986		if (sub) { mixin(S_TRACE);
 987			foreach (d; _subdir) { mixin(S_TRACE);
 988				d.sortSubDirs(true);
 989			}
 990		}
 991	}
 992	/// ditto
 993	void sortFlags(bool sub = false) { mixin(S_TRACE);
 994		bool cmps(in Flag a, in Flag b) { mixin(S_TRACE);
 995			if (_sorter) { mixin(S_TRACE);
 996				return _sorter(a.name, b.name) < 0;
 997			} else { mixin(S_TRACE);
 998				return cmp(a.name, b.name) < 0;
 999			}
1000		}
1001		if (!isSortedDlg!(Flag)(_flags, &cmps)) { mixin(S_TRACE);
1002			if (_change) _change();
1003			_flags = sortDlg!(Flag)(_flags, &cmps);
1004		}
1005		if (sub) { mixin(S_TRACE);
1006			foreach (d; _subdir) { mixin(S_TRACE);
1007				d.sortFlags(true);
1008			}
1009		}
1010	}
1011	/// ditto
1012	void sortSteps(bool sub = false) { mixin(S_TRACE);
1013		bool cmps(in Step a, in Step b) { mixin(S_TRACE);
1014			if (_sorter) { mixin(S_TRACE);
1015				return _sorter(a.name, b.name) < 0;
1016			} else { mixin(S_TRACE);
1017				return cmp(a.name, b.name) < 0;
1018			}
1019		}
1020		if (!isSortedDlg!(Step)(_steps, &cmps)) { mixin(S_TRACE);
1021			if (_change) _change();
1022			_steps = sortDlg!(Step)(_steps, &cmps);
1023		}
1024		if (sub) { mixin(S_TRACE);
1025			foreach (d; _subdir) { mixin(S_TRACE);
1026				d.sortSteps(true);
1027			}
1028		}
1029	}
1030
1031	/// ?????????????????
1032	@property
1033	const
1034	string path() { mixin(S_TRACE);
1035		if (_parent !is null) { mixin(S_TRACE);
1036			return _parent.path ~ _name ~ SEPARATOR;
1037		} else { mixin(S_TRACE);
1038			return "";
1039		}
1040	}
1041	/// ?????????????????????????????????????
1042	void toNode(ref XNode e) { mixin(S_TRACE);
1043		auto fe = e.newElement("Flags");
1044		toNodeFlags(fe);
1045		auto se = e.newElement("Steps");
1046		toNodeSteps(se);
1047	}
1048	private void toNodeFlags(ref XNode e) { mixin(S_TRACE);
1049		foreach (flag; flags) { mixin(S_TRACE);
1050			flag.toNode(e);
1051			foreach (dir; _subdir) { mixin(S_TRACE);
1052				dir.toNodeFlags(e);
1053			}
1054		}
1055	}
1056	private void toNodeSteps(ref XNode e) { mixin(S_TRACE);
1057		foreach (step; steps) { mixin(S_TRACE);
1058			step.toNode(e);
1059			foreach (dir; _subdir) { mixin(S_TRACE);
1060				dir.toNodeSteps(e);
1061			}
1062		}
1063	}
1064
1065	/// ??????????????????????????????
1066	/// ????????
1067	static string basename(string path) { mixin(S_TRACE);
1068		int sepLen = SEPARATOR.length;
1069		if (path.length < sepLen) { mixin(S_TRACE);
1070			return path;
1071		}
1072		if (endsWith(path, SEPARATOR.dup)) { mixin(S_TRACE);
1073			path = path[0 .. $ - sepLen];
1074		}
1075		for (int i = path.length - sepLen; i >= 0; i--) { mixin(S_TRACE);
1076			if (path[i .. i + sepLen] == SEPARATOR) { mixin(S_TRACE);
1077				return path[i + sepLen .. $];
1078			}
1079		}
1080		return path;
1081	} unittest { mixin(S_TRACE);
1082		debug mixin(UTPerf);
1083		assert (FlagDir.basename("\\test\\") == "test");
1084		assert (FlagDir.basename("\\aaa\\test") == "test");
1085		assert (FlagDir.basename("test") == "test");
1086		assert (FlagDir.basename("\\") == "");
1087		assert (FlagDir.basename("") == "");
1088	}
1089
1090	/// ???????????????
1091	/// ??????????????????????true????
1092	/// ????????????????????????
1093	bool has(string path) { mixin(S_TRACE);
1094		path = .toLower(path);
1095		auto tpath = .toLower(this.path);
1096		int len = path.length;
1097		int tlen = tpath.length;
1098		int sepLen = SEPARATOR.length;
1099		return (len <= tlen) && (tpath[0 .. len] == path);
1100	} unittest { mixin(S_TRACE);
1101		debug mixin(UTPerf);
1102		auto dir1 = new FlagDir(cast(CWXPath) null);
1103		auto dir2 = new FlagDir("aaaaA");
1104		dir1.add(dir2);
1105		auto dir3 = new FlagDir("fsadfawegGGGg");
1106		dir2.add(dir3);
1107
1108		auto dir4 = new FlagDir(cast(CWXPath) null);
1109		auto dir5 = new FlagDir("aAAAA");
1110		dir4.add(dir5);
1111		auto dir6 = new FlagDir("fsadFAwegGGGga");
1112		dir5.add(dir6);
1113
1114		assert (dir1.has(dir1.path));
1115		assert (!dir1.has(dir2.path));
1116		assert (!dir1.has(dir3.path));
1117		assert (dir1.has(dir4.path));
1118		assert (!dir1.has(dir5.path));
1119		assert (!dir1.has(dir6.path));
1120
1121		assert (dir2.has(dir1.path));
1122		assert (dir2.has(dir2.path));
1123		assert (!dir2.has(dir3.path));
1124		assert (dir2.has(dir4.path));
1125		assert (dir2.has(dir5.path));
1126		assert (!dir2.has(dir6.path));
1127
1128		assert (dir3.has(dir1.path));
1129		assert (dir3.has(dir2.path));
1130		assert (dir3.has(dir3.path));
1131		assert (dir3.has(dir4.path));
1132		assert (dir3.has(dir5.path));
1133		assert (!dir3.has(dir6.path));
1134
1135		assert (dir4.has(dir1.path));
1136		assert (!dir4.has(dir2.path));
1137		assert (!dir4.has(dir3.path));
1138		assert (dir4.has(dir4.path));
1139		assert (!dir4.has(dir5.path));
1140		assert (!dir4.has(dir6.path));
1141
1142		assert (dir5.has(dir1.path));
1143		assert (dir5.has(dir2.path));
1144		assert (!dir5.has(dir3.path));
1145		assert (dir5.has(dir4.path));
1146		assert (dir5.has(dir5.path));
1147		assert (!dir5.has(dir6.path));
1148
1149		assert (dir6.has(dir1.path));
1150		assert (dir6.has(dir2.path));
1151		assert (!dir6.has(dir3.path));
1152		assert (dir6.has(dir4.path));
1153		assert (dir6.has(dir5.path));
1154		assert (dir6.has(dir6.path));
1155	}
1156
1157	/// path????????????????????
1158	static string up(string path) { mixin(S_TRACE);
1159		int sepLen = SEPARATOR.length;
1160		if (path.length < sepLen) { mixin(S_TRACE);
1161			return null;
1162		}
1163		if (path[$ - sepLen .. $] == SEPARATOR) { mixin(S_TRACE);
1164			path = path[0 .. $ - sepLen];
1165		}
1166		for (int i = path.length - sepLen; i >= 0; i--) { mixin(S_TRACE);
1167			if (path[i .. i + sepLen] == SEPARATOR) { mixin(S_TRACE);
1168				return path[0 .. i + sepLen];
1169			}
1170		}
1171		return "";
1172	} unittest { mixin(S_TRACE);
1173		debug mixin(UTPerf);
1174		assert (FlagDir.up("test\\") == "");
1175		assert (FlagDir.up("\\aaa\\test") == "\\aaa\\");
1176		assert (FlagDir.up("\\aaa\\test\\t\\") == "\\aaa\\test\\");
1177		assert (FlagDir.up("test") == "");
1178		assert (FlagDir.up("") is null);
1179	}
1180
1181	/// appendFromXML()?????
1182	/// See_Also: appendFromXML()
1183	static enum AppendXmlResult {
1184		/// ???????????????
1185		DIR_SUCCESS,
1186		/// ?????????????????
1187		FLAG_STEP_SUCCESS,
1188		/// ????????
1189		FAIL,
1190		/// ?????????????????????????????
1191		FLAG_STEP_ON_DIR,
1192		/// ??????????????????????
1193		ON_DIR,
1194	}
1195
1196	private static bool __loadFS(string Fs, string Fg, F)
1197			(ref XNode node, FlagDir p, out F[string] c, string delegate(string, string) createNewName, bool copy, in XMLInfo ver) { mixin(S_TRACE);
1198		bool ret = true;
1199		node.onTag[Fs] = (ref XNode node) { mixin(S_TRACE);
1200			if (!ret) return;
1201			node.onTag[Fg] = (ref XNode n) { mixin(S_TRACE);
1202				if (!ret) return;
1203				auto f = F.createFromNode(n, ver);
1204				if (!p.canAppend!F(f.name)) { mixin(S_TRACE);
1205					if (copy) { mixin(S_TRACE);
1206						f.name = createNewName(f.name, "");
1207					} else { mixin(S_TRACE);
1208						ret = false;
1209						return;
1210					}
1211				}
1212				c[n.childText("Name", true)] = f;
1213			};
1214			node.parse();
1215		};
1216		node.parse();
1217		return ret;
1218	}
1219	private bool loadFlagAndSteps
1220			(ref XNode node, out Flag[string] cFlags, out Step[string] cSteps, bool copy, in XMLInfo ver) { mixin(S_TRACE);
1221		try { mixin(S_TRACE);
1222			if (!__loadFS!("Flags", "Flag", Flag)(node, this, cFlags, &createNewFlagName, copy, ver)) { mixin(S_TRACE);
1223				.removeAll(cFlags);
1224				.removeAll(cSteps);
1225				return false;
1226			}
1227			if (!__loadFS!("Steps", "Step", Step)(node, this, cSteps, &createNewStepName, copy, ver)) { mixin(S_TRACE);
1228				.removeAll(cFlags);
1229				.removeAll(cSteps);
1230				return false;
1231			}
1232			foreach (v; cFlags.values) { mixin(S_TRACE);
1233				this.add(v);
1234			}
1235			foreach (v; cSteps.values) { mixin(S_TRACE);
1236				this.add(v);
1237			}
1238			return true;
1239		} catch (Exception e) {
1240			.removeAll(cFlags);
1241			.removeAll(cSteps);
1242			return false;
1243		}
1244	}
1245	private FlagDir loadSubs
1246			(ref XNode node, out Flag[string] cFlags, out Step[string] cSteps, bool copy, in XMLInfo ver) { mixin(S_TRACE);
1247		try { mixin(S_TRACE);
1248			auto subName = basename(node.attr("path", true));
1249			if (subName.length == 0) { mixin(S_TRACE);
1250				subName = validName(node.attr("rootName", true));
1251			}
1252			if (!canAppendSub(subName)) { mixin(S_TRACE);
1253				if (copy) { mixin(S_TRACE);
1254					subName = createNewDirName(subName, "");
1255				} else { mixin(S_TRACE);
1256					return null;
1257				}
1258			}
1259			auto sub = new FlagDir(subName);
1260			if (!sub.loadFlagAndSteps(node, cFlags, cSteps, false, ver)) { mixin(S_TRACE);
1261				assert (cFlags.length == 0);
1262				assert (cSteps.length == 0);
1263				return null;
1264			}
1265			bool ret = true;
1266			node.onTag["FlagDirectory"] = (ref XNode n) { mixin(S_TRACE);
1267				if (!ret) return;
1268				if (!sub.loadSubs(n, cFlags, cSteps, false, ver)) { mixin(S_TRACE);
1269					ret = false;
1270					return;
1271				}
1272			};
1273			node.parse();
1274			if (ret) { mixin(S_TRACE);
1275				this.add(sub);
1276				return sub;
1277			}
1278		} catch (Exception e) {
1279		}
1280		.removeAll(cFlags);
1281		.removeAll(cSteps);
1282		return null;
1283	}
1284	private bool readAtt(in XNode node, out string rootId, out string path, out bool sameTree) { mixin(S_TRACE);
1285		path = null;
1286		sameTree = false;
1287		rootId = node.attr(XML_ATT_ROOT_ID, false);
1288		path = node.attr(XML_ATT_PATH, false);
1289		if (rootId !is null && path !is null) { mixin(S_TRACE);
1290			sameTree = this.root.id == rootId;
1291			return true;
1292		} else { mixin(S_TRACE);
1293			return false;
1294		}
1295	}
1296	/// XML???????????????????true????
1297	/// getXml()?????XML???????????false????
1298	/// ???copy = false??????????????false???:
1299	/// (1) ??????????????????(???????????????????????)?
1300	/// (2) ????????/???????????/?????????????
1301	/// (3) ????????????????????
1302	/// (4) ?????????????????????????????????????
1303	/// copy = true??????????????/???/???????????" (??)"?????
1304	/// Params:
1305	/// xml = XML??
1306	/// copy = ???????true???????false?
1307	/// dirMode = ?????????????????
1308	/// newPath = AppendXmlResult.DIR_SUCCESS???????????????????????????
1309	/// cFlags = ???????????????????????????????????
1310	/// cSteps = ?????????????????????????????????????
1311	/// Returns: XML????????????
1312	/// See_Also: getXml(FlagDir, Flag[], Step[]), getXml(FlagDir)
1313	AppendXmlResult appendFromXML(string xml, in XMLInfo ver, bool copy, bool dirMode,
1314			out Flag[string] cFlags, out Step[string] cSteps, out string newPath, out string rootId) { mixin(S_TRACE);
1315		newPath = null;
1316		rootId = "";
1317		try { mixin(S_TRACE);
1318			scope doc = XNode.parse(xml);
1319			rootId = doc.attr(XML_ATT_ROOT_ID, false, "");
1320
1321			if (doc.name == XML_ROOT_FLAGS_AND_STEPS) { mixin(S_TRACE);
1322				string path;
1323				bool sameTree;
1324				if (readAtt(doc, rootId, path, sameTree)) { mixin(S_TRACE);
1325					if (!copy && sameTree && icmp(this.path, path) == 0) { mixin(S_TRACE);
1326						// ???????????????????????????
1327						doc.onTag["Flags"] = (ref XNode node) { mixin(S_TRACE);
1328							doc.onTag["Flag"] = (ref XNode node) { mixin(S_TRACE);
1329								add(getFlag(node.childText("Name", true)));
1330							};
1331							node.parse();
1332						};
1333						doc.onTag["Steps"] = (ref XNode node) { mixin(S_TRACE);
1334							doc.onTag["Step"] = (ref XNode node) { mixin(S_TRACE);
1335								add(getStep(node.childText("Name", true)));
1336							};
1337							node.parse();
1338						};
1339						doc.parse();
1340						return AppendXmlResult.FLAG_STEP_ON_DIR;
1341					}
1342					if (loadFlagAndSteps(doc, cFlags, cSteps, copy, ver)) { mixin(S_TRACE);
1343						return AppendXmlResult.FLAG_STEP_SUCCESS;
1344					}
1345				}
1346				return AppendXmlResult.FAIL;
1347			}
1348			if (dirMode && doc.name == XML_ROOT_FLAG_DIRECTORY) { mixin(S_TRACE);
1349				return loadRootFlagDirectory(doc, ver, copy, cFlags, cSteps, newPath);
1350			}
1351		} catch (Exception e) {
1352		}
1353		return AppendXmlResult.FAIL;
1354	}
1355	private AppendXmlResult loadRootFlagDirectory(ref XNode node, in XMLInfo ver, bool copy,
1356			out Flag[string] cFlags, out Step[string] cSteps, out string newPath) { mixin(S_TRACE);
1357		newPath = null;
1358		string rootId;
1359		string path;
1360		bool sameTree;
1361		if (readAtt(node, rootId, path, sameTree)) { mixin(S_TRACE);
1362			auto dirName = basename(path);
1363			if (!copy) { mixin(S_TRACE);
1364				if (path.length == 0) { mixin(S_TRACE);
1365					// ??????????????
1366					return AppendXmlResult.FAIL;
1367				}
1368				if (sameTree && icmp(this.path, up(path)) == 0) { mixin(S_TRACE);
1369					// ???????????????????????????
1370					auto d = getSubDir(dirName);
1371					add(d);
1372					newPath = d.path;
1373					return AppendXmlResult.ON_DIR;
1374				}
1375				if (sameTree && has(path)) { mixin(S_TRACE);
1376					// ???????????????????
1377					return AppendXmlResult.FAIL;
1378				}
1379				if (!canAppendSub(dirName)) { mixin(S_TRACE);
1380					// ????????????????????
1381					return AppendXmlResult.FAIL;
1382				}
1383			}
1384			auto sub = loadSubs(node, cFlags, cSteps, copy, ver);
1385			if (sub) { mixin(S_TRACE);
1386				newPath = sub.path;
1387			}  else { mixin(S_TRACE);
1388				return AppendXmlResult.FAIL;
1389			}
1390			return AppendXmlResult.DIR_SUCCESS;
1391		}
1392		return AppendXmlResult.FAIL;
1393	}
1394
1395	/// ???????????????????????
1396	/// path??????????????
1397	static FlagDir searchPath(FlagDir root, string path) { mixin(S_TRACE);
1398		assert (root.parent is null);
1399		int sepLen = SEPARATOR.length;
1400		if (endsWith(path, FlagDir.SEPARATOR.dup)) { mixin(S_TRACE);
1401			path = path[0 .. $ - sepLen];
1402		}
1403		auto paths = std.string.split(path, SEPARATOR.dup);
1404		auto dir = root;
1405
1406		loop: for (int lev = 0; lev < paths.length; lev++) { mixin(S_TRACE);
1407			foreach (sub; dir.subDirs) { mixin(S_TRACE);
1408				if (icmp(paths[lev], sub.name) == 0) { mixin(S_TRACE);
1409					if (lev < paths.length - 1) { mixin(S_TRACE);
1410						dir = sub;
1411						continue loop;
1412					} else { mixin(S_TRACE);
1413						return sub;
1414					}
1415				}
1416			}
1417			break;
1418		}
1419		return null;
1420	}
1421
1422	/// ????????????????????????????
1423	/// ???????????
1424	/// ????????????????????????'\'??????????
1425	static string validName(string name) { mixin(S_TRACE);
1426		if (!name.length) { mixin(S_TRACE);
1427			name = "_";
1428		}
1429		string fsep = SEPARATOR;
1430		return replace(name, fsep, "");
1431	}
1432
1433	/// base?????????????????????????
1434	/// base = xxxx????xxxx???????????????xxxx (2)?
1435	/// ???xxxx (2)????????????xxxx (3)��???????
1436	/// ???????????????????
1437	string createNewFlagName(string base, string oldName) { mixin(S_TRACE);
1438		return createNewName(validName(base), (string name) { mixin(S_TRACE);
1439			if (oldName && oldName.length && oldName == name) return true;
1440			return canAppend!Flag(name);
1441		});
1442	}
1443	/// ditto
1444	string createNewStepName(string base, string oldName) { mixin(S_TRACE);
1445		return createNewName(validName(base), (string name) { mixin(S_TRACE);
1446			if (oldName && oldName.length && oldName == name) return true;
1447			return canAppend!Step(name);
1448		});
1449	}
1450	/// ditto
1451	string createNewDirName(string base, string oldName) { mixin(S_TRACE);
1452		return createNewName(validName(base), (string name) { mixin(S_TRACE);
1453			if (oldName && oldName.length && oldName == name) return true;
1454			return canAppendSub(name);
1455		});
1456	}
1457	/// ??????n????????
1458	private string[] createNewNames(F)(string base, size_t n, in string[] oldNames)
1459	out (value) { mixin(S_TRACE);
1460		assert (value.length == n);
1461	} body { mixin(S_TRACE);
1462		auto oldSet = new HashSet!string;
1463		foreach (name; oldNames) oldSet.add(name.toLower());
1464		auto set = new HashSet!string;
1465		string[] r;
1466		foreach (i; 0..n) { mixin(S_TRACE);
1467			auto name = createNewName(validName(base), (string name) { mixin(S_TRACE);
1468				return (canAppend!F(name) || oldSet.contains(name.toLower())) && !set.contains(name.toLower());
1469			});
1470			set.add(name.toLower());
1471			r ~= name;
1472		}
1473		return r;
1474	}
1475	/// ditto
1476	string[] createNewFlagNames(string base, size_t n, in string[] oldNames)
1477	out (value) { mixin(S_TRACE);
1478		assert (value.length == n);
1479	} body { mixin(S_TRACE);
1480		return createNewNames!Flag(base, n, oldNames);
1481	}
1482	/// ditto
1483	string[] createNewStepNames(string base, size_t n, in string[] oldNames)
1484	out (value) { mixin(S_TRACE);
1485		assert (value.length == n);
1486	} body { mixin(S_TRACE);
1487		return createNewNames!Step(base, n, oldNames);
1488	}
1489	/// ditto
1490	string[] createNewDirNames(string base, size_t n, in string[] oldNames)
1491	out (value) { mixin(S_TRACE);
1492		assert (value.length == n);
1493	} body { mixin(S_TRACE);
1494		return createNewNames!FlagDir(base, n, oldNames);
1495	}
1496
1497	/// ??????????????
1498	/// Params:
1499	/// path = ???
1500	/// create = true????????????????????
1501	/// Returns: ???????????????????????null?
1502	FlagDir findPath(string path, bool create) { mixin(S_TRACE);
1503		if (path.length == 0) { mixin(S_TRACE);
1504			return root;
1505		} else { mixin(S_TRACE);
1506			string fsep = SEPARATOR;
1507			auto paths = std.string.split(path, fsep);
1508			if (paths.length == 0) { mixin(S_TRACE);
1509				return root;
1510			}
1511			return root.__findPath(paths[0 .. $ - 1], create);
1512		}
1513	}
1514	const
1515	const(FlagDir) findPath(string path) { mixin(S_TRACE);
1516		if (path.length == 0) { mixin(S_TRACE);
1517			return root;
1518		} else { mixin(S_TRACE);
1519			string fsep = SEPARATOR;
1520			auto paths = std.string.split(path, fsep);
1521			if (paths.length == 0) { mixin(S_TRACE);
1522				return root;
1523			}
1524			return root.__findPath(paths[0 .. $ - 1]);
1525		}
1526	}
1527	private FlagDir __findPath(string[] paths, bool create) { mixin(S_TRACE);
1528		auto sub = getSubDir(paths[0]);
1529		if (sub is null) { mixin(S_TRACE);
1530			if (create) { mixin(S_TRACE);
1531				if (canAppendSub(paths[0])) { mixin(S_TRACE);
1532					sub = new FlagDir(paths[0]);
1533					this.add(sub);
1534				} else { mixin(S_TRACE);
1535					return null;
1536				}
1537			} else { mixin(S_TRACE);
1538				return null;
1539			}
1540		}
1541		if (paths.length == 1) { mixin(S_TRACE);
1542			return sub;
1543		} else { mixin(S_TRACE);
1544			return sub.__findPath(paths[1 .. $], create);
1545		}
1546	}
1547	const
1548	private const(FlagDir) __findPath(string[] paths) { mixin(S_TRACE);
1549		auto sub = getSubDir(paths[0]);
1550		if (sub is null) { mixin(S_TRACE);
1551			return null;
1552		}
1553		if (paths.length == 1) { mixin(S_TRACE);
1554			return sub;
1555		} else { mixin(S_TRACE);
1556			return sub.__findPath(paths[1 .. $]);
1557		}
1558	}
1559	/// ??????????????????
1560	/// Params:
1561	/// path = ???
1562	/// Returns: ????????????????????null?
1563	Flag findFlag(string path) { mixin(S_TRACE);
1564		if (path.length > 0) { mixin(S_TRACE);
1565			auto dir = findPath(up(path), false);
1566			if (dir !is null) { mixin(S_TRACE);
1567				return dir.getFlag(basename(path));
1568			}
1569		}
1570		return null;
1571	}
1572	/// ditto
1573	const
1574	const(Flag) findFlag(string path) { mixin(S_TRACE);
1575		if (path.length > 0) { mixin(S_TRACE);
1576			auto dir = findPath(up(path));
1577			if (dir !is null) { mixin(S_TRACE);
1578				return dir.getFlag(basename(path));
1579			}
1580		}
1581		return null;
1582	}
1583	/// ???????????????????
1584	/// Params:
1585	/// path = ???
1586	/// Returns: ?????????????????????null?
1587	Step findStep(string path) { mixin(S_TRACE);
1588		if (path.length > 0) { mixin(S_TRACE);
1589			auto dir = findPath(up(path), false);
1590			if (dir !is null) { mixin(S_TRACE);
1591				return dir.getStep(basename(path));
1592			}
1593		}
1594		return null;
1595	}
1596	const
1597	const(Step) findStep(string path) { mixin(S_TRACE);
1598		if (path.length > 0) { mixin(S_TRACE);
1599			auto dir = findPath(up(path));
1600			if (dir !is null) { mixin(S_TRACE);
1601				return dir.getStep(basename(path));
1602			}
1603		}
1604		return null;
1605	}
1606
1607	/// ??????????????????????????????
1608	const
1609	void toNodeAll(ref XNode node) { mixin(S_TRACE);
1610		auto fe = node.newElement("Flags");
1611		foreach (flag; allFlags) { mixin(S_TRACE);
1612			flag.toNode(fe);
1613		}
1614		auto se = node.newElement("Steps");
1615		foreach (step; allSteps) { mixin(S_TRACE);
1616			step.toNode(se);
1617		}
1618	}
1619
1620	/// XML????????????????????????????
1621	/// Params:
1622	/// node = ????
1623	/// change = ????????????
1624	/// Returns: ??????????
1625	static FlagDir fromXmlNode(ref XNode node, CWXPath owner, void delegate() change, in XMLInfo ver) { mixin(S_TRACE);
1626		auto root = new FlagDir(owner);
1627		node.onTag["Flags"] = (ref XNode node) { mixin(S_TRACE);
1628			__fromXmlNode!(Flag)(node, root, "Flag", &Flag.createFromNode, ver);
1629		};
1630		node.onTag["Steps"] = (ref XNode node) { mixin(S_TRACE);
1631			__fromXmlNode!(Step)(node, root, "Step", &Step.createFromNode, ver);
1632		};
1633		node.parse();
1634		root.changeHandler = change;
1635		return root;
1636	}
1637	private static void __fromXmlNode(E)(ref XNode node,
1638			FlagDir root, string es, E function(ref XNode, in XMLInfo) pfunc, in XMLInfo ver) { mixin(S_TRACE);
1639		node.onTag[es] = (ref XNode e) { mixin(S_TRACE);
1640			string path = e.childText("Name", false);
1641			if (path) { mixin(S_TRACE);
1642				auto parent = up(path);
1643				auto dir = parent !is null ? root.findPath(parent, true) : root;
1644				auto f = pfunc(e, ver);
1645				if (!f || !dir.canAppend!E(f.name)) { mixin(S_TRACE);
1646					throw new FlagException(es ~ " parse error: " ~ path);
1647				}
1648				dir.add(f);
1649			} else { mixin(S_TRACE);
1650				throw new FlagException(es ~ " name not found.");
1651			}
1652		};
1653		node.parse();
1654		root.sortSubDirs(true);
1655		root.sortFlags(true);
1656		root.sortSteps(true);
1657	}
1658
1659	/// ???????????????
1660	/// ?????????????????????
1661	/// uc??????????
1662	bool rename(string name, UseCounter uc) { mixin(S_TRACE);
1663		auto flags = allFlags;
1664		auto oldFlagPaths = new string[flags.length];
1665		foreach (i, flag; flags) { mixin(S_TRACE);
1666			oldFlagPaths[i] = flag.path;
1667		}
1668		auto steps = allSteps;
1669		auto oldStepPaths = new string[steps.length];
1670		foreach (i, step; steps) { mixin(S_TRACE);
1671			oldStepPaths[i] = step.path;
1672		}
1673		string p = this.path;
1674		size_t plen = p.length;
1675		if (!.endsWith(p, FlagDir.SEPARATOR.idup)) { mixin(S_TRACE);
1676			plen += FlagDir.SEPARATOR.length;
1677		}
1678
1679		if (!this.name(name)) { mixin(S_TRACE);
1680			return false;
1681		}
1682		p = this.path;
1683		foreach (path; oldFlagPaths) { mixin(S_TRACE);
1684			auto newPath = FlagDir.join(p, path[plen .. $]);
1685			uc.change(toFlagId(path), toFlagId(newPath));
1686		}
1687		foreach (path; oldStepPaths) { mixin(S_TRACE);
1688			auto newPath = FlagDir.join(p, path[plen .. $]);
1689			uc.change(toStepId(path), toStepId(newPath));
1690		}
1691		return true;
1692	}
1693}