PageRenderTime 77ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

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