StructureTest.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Data\Test\Unit;
  7. class StructureTest extends \PHPUnit\Framework\TestCase
  8. {
  9. /**
  10. * @var \Magento\Framework\Data\Structure
  11. */
  12. protected $_structure;
  13. /**
  14. * @return void
  15. */
  16. protected function setUp()
  17. {
  18. $this->_structure = new \Magento\Framework\Data\Structure();
  19. }
  20. /**
  21. * @param array $elements
  22. * @return void
  23. * @dataProvider importExportElementsDataProvider
  24. */
  25. public function testConstructImportExportElements($elements)
  26. {
  27. $this->assertSame([], $this->_structure->exportElements());
  28. $this->_structure->importElements($elements);
  29. $this->assertSame($elements, $this->_structure->exportElements());
  30. $structure = new \Magento\Framework\Data\Structure($elements);
  31. $this->assertSame($elements, $structure->exportElements());
  32. }
  33. /**
  34. * @return array
  35. */
  36. public function importExportElementsDataProvider()
  37. {
  38. return [
  39. [[]],
  40. [['element' => ['arbitrary_key' => 'value']]],
  41. [
  42. [
  43. 'one' => [\Magento\Framework\Data\Structure::CHILDREN => ['two' => 2, 'three' => 3]],
  44. 'two' => [\Magento\Framework\Data\Structure::PARENT => 'one'],
  45. 'three' => [\Magento\Framework\Data\Structure::PARENT => 'one'],
  46. 'four' => [\Magento\Framework\Data\Structure::CHILDREN => []],
  47. ]
  48. ],
  49. [
  50. [
  51. 'one' => [
  52. \Magento\Framework\Data\Structure::CHILDREN => ['two' => 't.w.o.'],
  53. \Magento\Framework\Data\Structure::GROUPS => [
  54. 'group' => ['two' => 'two', 'three' => 'three'],
  55. ],
  56. ],
  57. 'two' => [\Magento\Framework\Data\Structure::PARENT => 'one'],
  58. 'three' => [],
  59. ]
  60. ]
  61. ];
  62. }
  63. /**
  64. * @param array $elements
  65. * @return void
  66. * @dataProvider importExceptionDataProvider
  67. * @expectedException \Magento\Framework\Exception\LocalizedException
  68. */
  69. public function testImportException($elements)
  70. {
  71. $this->_structure->importElements($elements);
  72. }
  73. /**
  74. * @return array
  75. */
  76. public function importExceptionDataProvider()
  77. {
  78. return [
  79. 'numeric id' => [['element']],
  80. 'completely missing nested set' => [
  81. ['one' => [\Magento\Framework\Data\Structure::PARENT => 'two'], 'two' => []],
  82. ],
  83. 'messed up nested set' => [
  84. [
  85. 'one' => [\Magento\Framework\Data\Structure::PARENT => 'two'],
  86. 'two' => [\Magento\Framework\Data\Structure::CHILDREN => ['three' => 't.h.r.e.e.']],
  87. 'three' => [],
  88. ],
  89. ],
  90. 'nested set invalid data type' => [
  91. ['one' => [\Magento\Framework\Data\Structure::CHILDREN => '']],
  92. ],
  93. 'duplicate aliases' => [
  94. [
  95. 'one' => [
  96. \Magento\Framework\Data\Structure::CHILDREN => ['two' => 'alias', 'three' => 'alias'],
  97. ],
  98. 'two' => [\Magento\Framework\Data\Structure::PARENT => 'one'],
  99. 'three' => [\Magento\Framework\Data\Structure::PARENT => 'one'],
  100. ],
  101. ],
  102. 'missing reference back to parent' => [
  103. ['one' => [
  104. \Magento\Framework\Data\Structure::CHILDREN => ['two' => 't.w.o.'], ], 'two' => [],
  105. ],
  106. ],
  107. 'broken reference back to parent' => [
  108. [
  109. 'one' => [
  110. \Magento\Framework\Data\Structure::CHILDREN => ['two' => 't.w.o.', 'three' => 't.h.r.e.e.'],
  111. ],
  112. 'two' => [\Magento\Framework\Data\Structure::PARENT => 'three'],
  113. 'three' => [\Magento\Framework\Data\Structure::PARENT => 'one'],
  114. ],
  115. ],
  116. 'groups invalid data type' => [['one' => [\Magento\Framework\Data\Structure::GROUPS => '']]],
  117. 'group invalid data type' => [
  118. ['one' => [\Magento\Framework\Data\Structure::GROUPS => [1]]],
  119. ],
  120. 'asymmetric group' => [
  121. [
  122. 'one' => [\Magento\Framework\Data\Structure::GROUPS => ['two' => 'three']],
  123. 'two' => [],
  124. 'three' => [],
  125. ],
  126. ],
  127. 'group references to non-existing element' => [
  128. ['one' => [\Magento\Framework\Data\Structure::GROUPS => ['two' => 'two']]],
  129. ]
  130. ];
  131. }
  132. /**
  133. * @param array $elements
  134. * @return void
  135. * @dataProvider importExceptionElementNotFoundDataProvider
  136. * @expectedException \OutOfBoundsException
  137. */
  138. public function testImportExceptionElementNotFound($elements)
  139. {
  140. $this->_structure->importElements($elements);
  141. }
  142. /**
  143. * @return array
  144. */
  145. public function importExceptionElementNotFoundDataProvider()
  146. {
  147. return [
  148. 'non-existing parent' => [
  149. ['element' => [\Magento\Framework\Data\Structure::PARENT => 'unknown']],
  150. ],
  151. 'missing child' => [
  152. [
  153. 'one' => [
  154. \Magento\Framework\Data\Structure::CHILDREN => ['two' => 't.w.o.', 'three' => 't.h.r.e.e.'],
  155. ],
  156. 'two' => [\Magento\Framework\Data\Structure::PARENT => 'one'],
  157. ],
  158. ],
  159. ];
  160. }
  161. /**
  162. * @return void
  163. */
  164. public function testCreateGetHasElement()
  165. {
  166. $data = [uniqid() => uniqid()];
  167. $elementId = uniqid('id');
  168. $this->assertFalse($this->_structure->hasElement($elementId));
  169. $this->assertFalse($this->_structure->getElement($elementId));
  170. $this->_structure->createElement($elementId, $data);
  171. $this->assertTrue($this->_structure->hasElement($elementId));
  172. $this->assertSame($data, $this->_structure->getElement($elementId));
  173. }
  174. /**
  175. * @return void
  176. * @expectedException \Magento\Framework\Exception\LocalizedException
  177. */
  178. public function testCreateElementException()
  179. {
  180. $elementId = uniqid('id');
  181. $this->_structure->createElement($elementId, []);
  182. $this->_structure->createElement($elementId, []);
  183. }
  184. /**
  185. * @return void
  186. */
  187. public function testUnsetElement()
  188. {
  189. $this->_populateSampleStructure();
  190. // non-recursively
  191. $this->assertTrue($this->_structure->unsetElement('six', false));
  192. $this->assertFalse($this->_structure->unsetElement('six', false));
  193. $this->assertSame([5], $this->_structure->getElement('five'));
  194. // recursively
  195. $this->assertTrue($this->_structure->unsetElement('three'));
  196. $this->assertTrue($this->_structure->unsetElement('four'));
  197. $this->assertSame(['one' => [], 'five' => [5]], $this->_structure->exportElements());
  198. }
  199. /**
  200. * @return void
  201. */
  202. public function testSetGetAttribute()
  203. {
  204. $this->_populateSampleStructure();
  205. $this->assertFalse($this->_structure->getAttribute('two', 'non-existing'));
  206. $this->assertEquals('bar', $this->_structure->getAttribute('two', 'foo'));
  207. $value = uniqid();
  208. $this->_structure->setAttribute('two', 'non-existing', $value)->setAttribute('two', 'foo', $value);
  209. $this->assertEquals($value, $this->_structure->getAttribute('two', 'non-existing'));
  210. $this->assertEquals($value, $this->_structure->getAttribute('two', 'foo'));
  211. }
  212. /**
  213. * @return void
  214. * @expectedException \OutOfBoundsException
  215. */
  216. public function testSetAttributeNoElementException()
  217. {
  218. $this->_structure->setAttribute('non-existing', 'foo', 'bar');
  219. }
  220. /**
  221. * @param string $attribute
  222. * @return void
  223. * @expectedException \InvalidArgumentException
  224. * @dataProvider setAttributeArgumentExceptionDataProvider
  225. */
  226. public function testSetAttributeArgumentException($attribute)
  227. {
  228. $this->_structure->importElements(['element' => []]);
  229. $this->_structure->setAttribute('element', $attribute, 'value');
  230. }
  231. /**
  232. * @return array
  233. */
  234. public function setAttributeArgumentExceptionDataProvider()
  235. {
  236. return [
  237. [\Magento\Framework\Data\Structure::CHILDREN],
  238. [\Magento\Framework\Data\Structure::PARENT],
  239. [\Magento\Framework\Data\Structure::GROUPS]
  240. ];
  241. }
  242. /**
  243. * @return void
  244. * @expectedException \OutOfBoundsException
  245. */
  246. public function testGetAttributeNoElementException()
  247. {
  248. $this->_structure->getAttribute('non-existing', 'foo');
  249. }
  250. /**
  251. * @return void
  252. */
  253. public function testRenameElement()
  254. {
  255. $this->_populateSampleStructure();
  256. // rename element and see how children got updated
  257. $element = $this->_structure->getElement('four');
  258. $this->assertNotEmpty($element);
  259. $this->assertFalse($this->_structure->getElement('four.5'));
  260. $this->assertSame($this->_structure, $this->_structure->renameElement('four', 'four.5'));
  261. $this->assertSame($element, $this->_structure->getElement('four.5'));
  262. $this->assertEquals(
  263. 'four.5',
  264. $this->_structure->getAttribute('two', \Magento\Framework\Data\Structure::PARENT)
  265. );
  266. $this->assertEquals(
  267. 'four.5',
  268. $this->_structure->getAttribute('three', \Magento\Framework\Data\Structure::PARENT)
  269. );
  270. // rename element and see how parent got updated
  271. $this->_structure->renameElement('three', 'three.5');
  272. // first child
  273. $this->assertSame(['three.5' => 'th', 'two' => 'tw'], $this->_structure->getChildren('four.5'));
  274. $this->_structure->renameElement('two', 'two.5');
  275. // second and last child
  276. $this->assertSame(['three.5' => 'th', 'two.5' => 'tw'], $this->_structure->getChildren('four.5'));
  277. }
  278. /**
  279. * @return void
  280. */
  281. public function testSetAsChild()
  282. {
  283. $this->_populateSampleStructure();
  284. // default alias
  285. $this->_structure->setAsChild('two', 'one');
  286. $this->assertEquals('one', $this->_structure->getParentId('two'));
  287. $this->assertEquals(['two' => 'two'], $this->_structure->getChildren('one'));
  288. $this->assertEquals(['three' => 'th'], $this->_structure->getChildren('four'));
  289. // specified alias
  290. $this->_structure->setAsChild('six', 'three', 's');
  291. $this->assertEquals('three', $this->_structure->getParentId('six'));
  292. $this->assertEquals(['six' => 's'], $this->_structure->getChildren('three'));
  293. }
  294. /**
  295. * @param int $offset
  296. * @param int $expectedOffset
  297. * @return void
  298. * @dataProvider setAsChildOffsetDataProvider
  299. */
  300. public function testSetAsChildOffset($offset, $expectedOffset)
  301. {
  302. $this->_populateSampleSortStructure();
  303. $this->_structure->setAsChild('x', 'parent', '', $offset);
  304. $children = $this->_structure->getChildren('parent');
  305. $actualOffset = array_search('x', array_keys($children));
  306. $this->assertSame(
  307. $expectedOffset,
  308. $actualOffset,
  309. "The 'x' is expected to be at '{$expectedOffset}' offset, rather than '{$actualOffset}', in array: " .
  310. var_export(
  311. $children,
  312. 1
  313. )
  314. );
  315. }
  316. /**
  317. * @return array
  318. */
  319. public function setAsChildOffsetDataProvider()
  320. {
  321. return [
  322. [0, 0],
  323. [1, 1],
  324. [2, 2],
  325. [3, 3],
  326. [4, 4],
  327. [5, 5],
  328. [null, 5],
  329. [-1, 4],
  330. [-2, 3],
  331. [-3, 2],
  332. [-4, 1],
  333. [-5, 0]
  334. ];
  335. }
  336. /**
  337. * @param string $elementId
  338. * @param string $parentId
  339. * @return void
  340. * @expectedException \Magento\Framework\Exception\LocalizedException
  341. * @dataProvider setAsChildExceptionDataProvider
  342. */
  343. public function testSetAsChildException($elementId, $parentId)
  344. {
  345. $this->_structure->createElement('one', []);
  346. $this->_structure->createElement('two', []);
  347. $this->_structure->createElement('three', []);
  348. $this->_structure->setAsChild('three', 'two');
  349. $this->_structure->setAsChild('two', 'one');
  350. $this->_structure->setAsChild($elementId, $parentId);
  351. }
  352. /**
  353. * @return array
  354. */
  355. public function setAsChildExceptionDataProvider()
  356. {
  357. return [['one', 'three'], ['one', 'one']];
  358. }
  359. /**
  360. * @return void
  361. */
  362. public function testUnsetChild()
  363. {
  364. $this->_populateSampleStructure();
  365. // specify element by name
  366. $this->_structure->unsetChild('five');
  367. $this->assertFalse($this->_structure->getParentId('five'));
  368. $this->assertArrayNotHasKey(\Magento\Framework\Data\Structure::CHILDREN, $this->_structure->getElement('six'));
  369. // specify element by parent and alias
  370. $this->_structure->unsetChild('four', 'tw');
  371. $this->assertFalse($this->_structure->getChildId('four', 'tw'));
  372. $this->assertFalse($this->_structure->getParentId('two'));
  373. }
  374. /**
  375. * @param int $initialOffset
  376. * @param int $newOffset
  377. * @param int $expectedOffset
  378. * @return void
  379. * @dataProvider reorderChildDataProvider
  380. */
  381. public function testReorderChild($initialOffset, $newOffset, $expectedOffset)
  382. {
  383. $this->_populateSampleSortStructure();
  384. $this->_structure->setAsChild('x', 'parent', '', $initialOffset);
  385. $this->assertSame($expectedOffset, $this->_structure->reorderChild('parent', 'x', $newOffset));
  386. }
  387. /**
  388. * @return array
  389. */
  390. public function reorderChildDataProvider()
  391. {
  392. return [
  393. // x* 1 2 3 4 5
  394. [0, 0, 1],
  395. [0, 1, 1],
  396. [0, 2, 2],
  397. [0, 3, 3],
  398. [0, +100500, 6],
  399. [0, -1, 5],
  400. [0, -4, 2],
  401. [0, -5, 1],
  402. [0, -999, 1],
  403. // 1 x* 2 3 4 5
  404. [1, 0, 1],
  405. [1, 1, 2],
  406. [1, 2, 2],
  407. [1, 3, 3],
  408. [1, -1, 5],
  409. [1, -4, 2],
  410. [1, -5, 2],
  411. [1, -6, 1],
  412. // 1 2 x* 3 4 5
  413. [2, 0, 1],
  414. [2, 1, 2],
  415. [2, 2, 3],
  416. [2, 3, 3],
  417. [2, 4, 4],
  418. [2, null, 6],
  419. // 1 2 3 4 5 x*
  420. [5, 0, 1],
  421. [5, 1, 2],
  422. [5, 5, 6]
  423. ];
  424. }
  425. /**
  426. * @return void
  427. * @expectedException \Magento\Framework\Exception\LocalizedException
  428. */
  429. public function testReorderChildException()
  430. {
  431. $this->_structure->createElement('one', []);
  432. $this->_structure->createElement('two', []);
  433. $this->_structure->reorderChild('one', 'two', 0);
  434. }
  435. /**
  436. * @param int $initialOffset
  437. * @param string $sibling
  438. * @param int $delta
  439. * @param int $expectedOffset
  440. * @return void
  441. * @dataProvider reorderSiblingDataProvider
  442. */
  443. public function testReorderToSibling($initialOffset, $sibling, $delta, $expectedOffset)
  444. {
  445. $this->_populateSampleSortStructure();
  446. $this->_structure->setAsChild('x', 'parent', '', $initialOffset);
  447. $this->assertSame($expectedOffset, $this->_structure->reorderToSibling('parent', 'x', $sibling, $delta));
  448. }
  449. /**
  450. * @return array
  451. */
  452. public function reorderSiblingDataProvider()
  453. {
  454. return [
  455. // x* 1 2 3 4 5
  456. [0, 'one', 1, 2],
  457. [0, 'three', 2, 5],
  458. [0, 'five', 1, 6],
  459. [0, 'five', 10, 6],
  460. [0, 'one', -1, 1],
  461. [0, 'one', -999, 1],
  462. // 1 2 x* 3 4 5
  463. [2, 'two', 1, 3],
  464. [2, 'two', 2, 4],
  465. [2, 'two', 3, 5],
  466. [2, 'two', 999, 6],
  467. [2, 'two', -1, 2],
  468. [2, 'two', -2, 1],
  469. [2, 'two', -999, 1],
  470. [2, 'x', 1, 3],
  471. [2, 'x', 2, 4],
  472. [2, 'x', 3, 5],
  473. [2, 'x', 999, 6],
  474. [2, 'x', -1, 3],
  475. [2, 'x', -2, 2],
  476. [2, 'x', -999, 1]
  477. ];
  478. }
  479. /**
  480. * @return void
  481. * @expectedException \Magento\Framework\Exception\LocalizedException
  482. */
  483. public function testReorderToSiblingException()
  484. {
  485. $this->_structure->createElement('one', []);
  486. $this->_structure->createElement('two', []);
  487. $this->_structure->createElement('three', []);
  488. $this->_structure->setAsChild('two', 'one');
  489. $this->_structure->reorderToSibling('one', 'three', 'two', 1);
  490. }
  491. /**
  492. * @return void
  493. */
  494. public function testGetChildId()
  495. {
  496. $this->_populateSampleStructure();
  497. $this->assertFalse($this->_structure->getChildId('nonexisting-parent', 'does not matter'));
  498. $this->assertEquals('five', $this->_structure->getChildId('six', 'f'));
  499. }
  500. /**
  501. * @return void
  502. */
  503. public function testGetChildrenParentIdChildAlias()
  504. {
  505. $this->_structure->createElement('one', []);
  506. $this->_structure->createElement('two', []);
  507. $this->_structure->createElement('three', []);
  508. $this->_structure->setAsChild('two', 'one');
  509. $this->_structure->setAsChild('three', 'one', 'th');
  510. // getChildren()
  511. $this->assertSame(['two' => 'two', 'three' => 'th'], $this->_structure->getChildren('one'));
  512. $this->assertSame([], $this->_structure->getChildren('three'));
  513. $this->assertSame([], $this->_structure->getChildren('nonexisting'));
  514. // getParentId()
  515. $this->assertEquals('one', $this->_structure->getParentId('two'));
  516. $this->assertFalse($this->_structure->getParentId('nonexistent'));
  517. // getChildAlias()
  518. $this->assertEquals('two', $this->_structure->getChildAlias('one', 'two'));
  519. $this->assertEquals('th', $this->_structure->getChildAlias('one', 'three'));
  520. $this->assertFalse($this->_structure->getChildAlias('nonexistent', 'child'));
  521. $this->assertFalse($this->_structure->getChildAlias('one', 'nonexistent'));
  522. }
  523. /**
  524. * @return void
  525. * @covers \Magento\Framework\Data\Structure::addToParentGroup
  526. * @covers \Magento\Framework\Data\Structure::getGroupChildNames
  527. */
  528. public function testGroups()
  529. {
  530. // non-existing element
  531. $this->assertFalse($this->_structure->addToParentGroup('non-existing', 'group1'));
  532. $this->assertSame([], $this->_structure->getGroupChildNames('non-existing', 'group1'));
  533. // not a child
  534. $this->_structure->createElement('one', []);
  535. $this->_structure->createElement('two', []);
  536. $this->assertFalse($this->_structure->addToParentGroup('two', 'group1'));
  537. $this->assertSame([], $this->_structure->getGroupChildNames('one', 'group1'));
  538. // child
  539. $this->_structure->setAsChild('two', 'one');
  540. $this->assertTrue($this->_structure->addToParentGroup('two', 'group1'));
  541. $this->assertTrue($this->_structure->addToParentGroup('two', 'group2'));
  542. // group getter
  543. $this->_structure->createElement('three', []);
  544. $this->_structure->createElement('four', []);
  545. $this->_structure->setAsChild('three', 'one', 'th');
  546. $this->_structure->setAsChild('four', 'one');
  547. $this->_structure->addToParentGroup('three', 'group1');
  548. $this->_structure->addToParentGroup('four', 'group2');
  549. $this->assertSame(['two', 'three'], $this->_structure->getGroupChildNames('one', 'group1'));
  550. $this->assertSame(['two', 'four'], $this->_structure->getGroupChildNames('one', 'group2'));
  551. // unset a child
  552. $this->_structure->unsetChild('one', 'two');
  553. $this->assertSame(['three'], $this->_structure->getGroupChildNames('one', 'group1'));
  554. $this->assertSame(['four'], $this->_structure->getGroupChildNames('one', 'group2'));
  555. // return child back
  556. $this->_structure->setAsChild('two', 'one');
  557. $this->assertSame(['two', 'three'], $this->_structure->getGroupChildNames('one', 'group1'));
  558. $this->assertSame(['two', 'four'], $this->_structure->getGroupChildNames('one', 'group2'));
  559. }
  560. /**
  561. * Import a sample valid structure
  562. * @return void
  563. */
  564. protected function _populateSampleStructure()
  565. {
  566. $this->_structure->importElements(
  567. [
  568. 'one' => [],
  569. 'two' => [\Magento\Framework\Data\Structure::PARENT => 'four', 'foo' => 'bar'],
  570. 'three' => [\Magento\Framework\Data\Structure::PARENT => 'four', 'bar' => 'baz'],
  571. 'four' => [\Magento\Framework\Data\Structure::CHILDREN => ['three' => 'th', 'two' => 'tw']],
  572. 'five' => [\Magento\Framework\Data\Structure::PARENT => 'six', 5],
  573. 'six' => [\Magento\Framework\Data\Structure::CHILDREN => ['five' => 'f']],
  574. ]
  575. );
  576. }
  577. /**
  578. * Import a sample structure, suitable for testing elements sort order
  579. * @return void
  580. */
  581. protected function _populateSampleSortStructure()
  582. {
  583. $child = [\Magento\Framework\Data\Structure::PARENT => 'parent'];
  584. $this->_structure->importElements(
  585. [
  586. 'parent' => [
  587. \Magento\Framework\Data\Structure::CHILDREN => [
  588. 'one' => 'e1',
  589. 'two' => 'e2',
  590. 'three' => 'e3',
  591. 'four' => 'e4',
  592. 'five' => 'e5',
  593. ],
  594. ],
  595. 'one' => $child,
  596. 'two' => $child,
  597. 'three' => $child,
  598. 'four' => $child,
  599. 'five' => $child,
  600. 'x' => [],
  601. ]
  602. );
  603. }
  604. }