001 // jademx - JADE management using JMX 002 // Copyright 2004-2005 Caboodle Networks, Inc. 003 // 004 // This library is free software; you can redistribute it and/or 005 // modify it under the terms of the GNU Lesser General Public 006 // License as published by the Free Software Foundation; either 007 // version 2.1 of the License, or (at your option) any later version. 008 // 009 // This library is distributed in the hope that it will be useful, 010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 // Lesser General Public License for more details. 013 // 014 // You should have received a copy of the GNU Lesser General Public 015 // License along with this library; if not, write to the Free Software 016 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 017 018 package jade.jademx.util.iso8601; 019 020 import junit.framework.Test; 021 import junit.framework.TestCase; 022 import junit.framework.TestSuite; 023 import jade.jademx.JadeMXSuiteTest; 024 025 import java.util.Locale; 026 027 /** 028 * test com.caboodlenetworks.agents.ontology.ui.AclMsgCmp 029 * @author David Bernstein, <a href="http://www.caboodlenetworks.com" 030 * >Caboodle Networks, Inc.</a> 031 */ 032 public class DurationTest extends TestCase { 033 034 035 // test various duration values 036 037 /** 038 * Test PT0S 039 */ 040 public void testPT0S() { 041 Duration d = new Duration("PT0S"); 042 assertEquals( "PT0S returned as "+d.toSeconds()+" seconds", 043 0, d.toSeconds() ); 044 assertEquals( "PT0S returned as "+d.toMilliseconds()+" milliseconds", 045 0, d.toMilliseconds() ); 046 assertEquals( "PT0S returned as \""+d.toString()+"\"", 047 "PT0S", d.toString() ); 048 } 049 050 /** 051 * Test PT5S 052 */ 053 public void testPT5S() { 054 Duration d = new Duration("PT5S"); 055 assertEquals( "PT5S returned as "+d.toSeconds()+" seconds", 056 5, d.toSeconds() ); 057 assertEquals( "PT5S returned as "+d.toMilliseconds()+" milliseconds", 058 5000, d.toMilliseconds() ); 059 assertEquals( "PT5S returned as \""+d.toString()+"\"", 060 "PT5S", d.toString() ); 061 } 062 063 /** 064 * Test PT7.4S 065 */ 066 public void testPT7pt4S() { 067 Duration d = new Duration("PT7.4S"); 068 assertEquals( "PT7.4S returned as "+d.toSeconds()+" seconds", 069 7, d.toSeconds() ); 070 assertEquals( "PT7.4S returned as "+d.toMilliseconds()+" milliseconds", 071 7400, d.toMilliseconds() ); 072 d.setLocale( Locale.US ); 073 assertEquals( "PT7.4S returned as \""+d.toString()+"\"", 074 "PT7.4S", d.toString() ); 075 } 076 077 /** 078 * Test PT7,4S 079 */ 080 public void testPT7cm4S() { 081 Duration d = new Duration("PT7,4S"); 082 assertEquals( "PT7,4S returned as "+d.toSeconds()+" seconds", 083 7, d.toSeconds() ); 084 assertEquals( "PT7,4S returned as "+d.toMilliseconds()+" milliseconds", 085 7400, d.toMilliseconds() ); 086 d.setLocale( Locale.US ); 087 assertEquals( "PT7,4S returned as \""+d.toString()+"\" [Locale=US]", 088 "PT7.4S", d.toString() ); 089 d.setLocale( Locale.GERMANY ); 090 assertEquals( "PT7,4S returned as \""+d.toString()+"\" [Locale=GERMANY]", 091 "PT7,4S", d.toString() ); 092 } 093 094 /** 095 * Test PT9M 096 */ 097 public void testPT9M() { 098 Duration d = new Duration("PT9M"); 099 assertEquals( "PT9M returned as "+d.toSeconds()+" seconds", 100 9 * 60, d.toSeconds() ); 101 assertEquals( "PT9M returned as "+d.toMilliseconds()+" milliseconds", 102 9 * 60 * 1000, d.toMilliseconds() ); 103 assertEquals( "PT9M returned as \""+d.toString()+"\"", 104 "PT9M", d.toString() ); 105 } 106 107 /** 108 * test locale get/set 109 */ 110 public void testDurationLocale() { 111 Duration d = new Duration("PT1S"); 112 d.setLocale( Locale.US ); 113 assertEquals( Locale.US, d.getLocale() ); 114 } 115 116 /** 117 * test non-week field init/get/set 118 */ 119 public void testDurationFieldsNonWeeks() { 120 121 Duration d = new Duration(); 122 123 assertFalse( d.isWeeks() ); 124 assertEquals( 0, d.getYears() ); 125 assertEquals( 0, d.getMonths() ); 126 assertEquals( 0, d.getDays() ); 127 assertEquals( 0, d.getHours() ); 128 assertEquals( 0, d.getMinutes() ); 129 assertEquals( 0, d.getSeconds() ); 130 131 d.setYears( 1 ); 132 d.setMonths( 2 ); 133 d.setDays( 3 ); 134 d.setHours( 4 ); 135 d.setMinutes( 5 ); 136 d.setSeconds( 6 ); 137 138 assertFalse( d.isWeeks() ); 139 assertEquals( 1, d.getYears() ); 140 assertEquals( 2, d.getMonths() ); 141 assertEquals( 3, d.getDays() ); 142 assertEquals( 4, d.getHours() ); 143 assertEquals( 5, d.getMinutes() ); 144 assertEquals( 6, d.getSeconds() ); 145 146 } 147 148 149 /** 150 * test week field init/get/set 151 */ 152 public void testDurationFieldWeeks() { 153 154 Duration d = new Duration(); 155 156 assertFalse( d.isWeeks() ); 157 assertEquals( 0, d.getWeeks() ); 158 159 d.setWeeks( 0 ); 160 assertTrue( d.isWeeks() ); 161 assertEquals( 0, d.getWeeks() ); 162 163 d.setWeeks( 1 ); 164 assertTrue( d.isWeeks() ); 165 assertEquals( 1, d.getWeeks() ); 166 167 168 169 } 170 171 /** 172 * test toString() 173 */ 174 public void testToString() { 175 176 Duration d = new Duration("P1Y2M3DT4H5M6.7S"); 177 assertEquals("P1Y2M3DT4H5M6.7S", d.toString()); 178 179 d.setYears( 7 ); 180 d.setMonths( 6 ); 181 d.setDays( 5 ); 182 d.setHours( 4 ); 183 d.setMinutes( 3 ); 184 d.setSeconds( 2 ); 185 try { 186 d.setFraction( Duration.SECOND_FRACTION, "1" ); 187 fail("didn't get not-yet-implemented exception for setting fraction"); 188 assertEquals("P7Y6M5DT4H3M2.1S", d.toString()); 189 } 190 catch ( RuntimeException re ) { 191 assertTrue(true); 192 } 193 } 194 195 196 /** 197 * test toString() for weeks 198 */ 199 public void testToStringWeeks() { 200 201 Duration d; 202 try { 203 d = new Duration("P2W"); 204 fail("no not-yet-implemented runtime exception trying to parse weeks"); 205 assertEquals("P2W", d.toString()); 206 } 207 catch ( RuntimeException re ) { 208 assertTrue(true); 209 } 210 211 d = new Duration(); 212 d.setWeeks( 5 ); 213 assertEquals("P5W", d.toString()); 214 215 } 216 217 /** 218 * test alternative format 219 */ 220 public void testAlternative() { 221 Duration d = new Duration("P1Y2M3DT4H5M6.7S"); 222 assertFalse( d.isAlternative() ); 223 assertFalse( d.isExtended() ); 224 d.setAlternative( true ); 225 assertTrue( d.isAlternative() ); 226 assertFalse( d.isExtended() ); 227 assertEquals("P00010203040506.7", d.toString()); 228 } 229 230 /** 231 * test alternative extended format 232 */ 233 public void testAlternativeExtended() { 234 Duration d = new Duration("P1Y2M3DT4H5M6.7S"); 235 assertFalse( d.isAlternative() ); 236 assertFalse( d.isExtended() ); 237 d.setAlternative( true ); 238 d.setExtended( true ); 239 assertTrue( d.isAlternative() ); 240 assertTrue( d.isExtended() ); 241 assertEquals("P0001-02-03T04:05:06.7", d.toString()); 242 } 243 244 245 /** 246 * test alternative extended format 247 */ 248 public void testExtended() { 249 Duration d = new Duration("P1Y2M3DT4H5M6.7S"); 250 assertFalse( d.isAlternative() ); 251 assertFalse( d.isExtended() ); 252 d.setExtended( true ); 253 assertFalse( d.isAlternative() ); 254 assertTrue( d.isExtended() ); 255 assertEquals("P1Y2M3DT4H5M6.7S", d.toString()); 256 } 257 258 259 /** 260 * test alternative format for weeks 261 */ 262 public void testAlternativeWeeks() { 263 Duration d = new Duration(); 264 d.setWeeks( 9 ); 265 assertFalse( d.isAlternative() ); 266 assertFalse( d.isExtended() ); 267 try { 268 d.setAlternative( true ); 269 fail("no runtime exception trying to set alternative format weeks"); 270 } 271 catch ( RuntimeException re ) { 272 assertTrue(true); 273 } 274 275 d = new Duration(); 276 d.setAlternative( true ); 277 try { 278 d.setWeeks( 9 ); 279 fail("no runtime exception trying to set alternative format weeks"); 280 } 281 catch ( RuntimeException re ) { 282 assertTrue(true); 283 } 284 } 285 286 287 288 /** 289 * test extended format for weeks 290 */ 291 public void testExtendedWeeks() { 292 Duration d = new Duration(); 293 d.setWeeks( 9 ); 294 assertFalse( d.isAlternative() ); 295 assertFalse( d.isExtended() ); 296 d.setExtended( true ); 297 assertFalse( d.isAlternative() ); 298 assertTrue( d.isExtended() ); 299 assertEquals("P9W", d.toString()); 300 } 301 302 /** 303 * look for initial 'P' 304 */ 305 public void testInitialP() { 306 try { 307 new Duration("x5H"); 308 fail("didn't catch missing initial P"); 309 } 310 catch ( IllegalArgumentException iae ) { 311 assertTrue(true); 312 } 313 } 314 315 /** 316 * try no field in string 317 */ 318 public void testNoField() { 319 try { 320 new Duration("P"); 321 fail("didn't catch missing fields"); 322 } 323 catch ( IllegalArgumentException iae ) { 324 assertTrue(true); 325 } 326 } 327 328 329 /** 330 * try fraction before 'T' 331 */ 332 public void testPreTFraction() { 333 try { 334 new Duration("P5.4D"); 335 fail("didn't throw exception on unimplemented fraction before T"); 336 } 337 catch ( RuntimeException re ) { 338 assertTrue(true); 339 } 340 } 341 342 /** 343 * try no unit in string 344 */ 345 public void testNoUnit() { 346 try { 347 new Duration("P6"); 348 fail("didn't catch missing unit"); 349 } 350 catch ( IllegalArgumentException iae ) { 351 assertTrue(true); 352 } 353 } 354 355 356 /** 357 * try unimplemented alternative extended format 358 */ 359 public void testAltExt() { 360 try { 361 new Duration("P0001-02-03T04:05:06"); 362 fail("didn't catch unimplemented alternative extended format"); 363 } 364 catch ( RuntimeException re ) { 365 assertTrue(true); 366 } 367 } 368 369 370 /** 371 * try unimplemented alternative baseic format 372 */ 373 public void testAltBasic() { 374 try { 375 new Duration("P00010203T040506"); 376 fail("didn't catch unimplemented alternative basic format"); 377 } 378 catch ( RuntimeException re ) { 379 assertTrue(true); 380 } 381 } 382 383 /** 384 * test PT3M 385 */ 386 public void testPT3M() { 387 Duration d = new Duration("P3M"); 388 assertEquals( 3, d.getMonths() ); 389 assertEquals( "P3M", d.toString() ); 390 } 391 392 393 /** 394 * test P6D 395 */ 396 public void testP6D() { 397 Duration d = new Duration("P6D"); 398 assertEquals( 6, d.getDays() ); 399 assertEquals( "P6D", d.toString() ); 400 } 401 402 403 /** 404 * try P5ST3D 405 */ 406 public void testP5ST3D() { 407 try { 408 new Duration("P5ST3D"); 409 fail("didn't catch bad unit before T"); 410 } 411 catch ( RuntimeException re ) { 412 assertTrue(true); 413 } 414 } 415 416 /** 417 * test P6DT5H 418 */ 419 public void testP6DT5H() { 420 Duration d = new Duration("P6DT5H"); 421 assertEquals( 6, d.getDays() ); 422 assertEquals( 5, d.getHours() ); 423 assertEquals( "P6DT5H", d.toString() ); 424 } 425 426 /** 427 * test P1Y2M 428 */ 429 public void testP1Y2M() { 430 Duration d = new Duration("P1Y2M"); 431 assertEquals( 1, d.getYears() ); 432 assertEquals( 2, d.getMonths() ); 433 assertEquals( 0, d.getDays() ); 434 assertEquals( "P1Y2M", d.toString() ); 435 } 436 437 /** 438 * test P1Y2D 439 */ 440 public void testP1Y2D() { 441 Duration d = new Duration("P1Y2D"); 442 assertEquals( 1, d.getYears() ); 443 assertEquals( 0, d.getMonths() ); 444 assertEquals( 2, d.getDays() ); 445 assertEquals( "P1Y2D", d.toString() ); 446 } 447 448 /** 449 * try P8D2M 450 */ 451 public void testP8D2M() { 452 try { 453 new Duration("P8D2M"); 454 fail("didn't catch month after day"); 455 } 456 catch ( RuntimeException re ) { 457 assertTrue(true); 458 } 459 } 460 461 462 /** 463 * try P8D2D 464 */ 465 public void testP8D2D() { 466 try { 467 new Duration("P8D2D"); 468 fail("didn't catch day after day"); 469 } 470 catch ( RuntimeException re ) { 471 assertTrue(true); 472 } 473 } 474 475 476 /** 477 * try P8D2S 478 */ 479 public void testP8D2S() { 480 try { 481 new Duration("P8D2S"); 482 fail("didn't catch second after day without T"); 483 } 484 catch ( RuntimeException re ) { 485 assertTrue(true); 486 } 487 } 488 489 490 491 /** 492 * try P5Y12D5D 493 */ 494 public void testP5Y12D5D() { 495 try { 496 new Duration("P5Y12D5D"); 497 fail("didn't catch year, day, day sequence"); 498 } 499 catch ( RuntimeException re ) { 500 assertTrue(true); 501 } 502 } 503 504 505 506 /** 507 * try P5Y12D5S 508 */ 509 public void testP5Y12D5S() { 510 try { 511 new Duration("P5Y12D5S"); 512 fail("didn't catch year, day, second sequence without T"); 513 } 514 catch ( RuntimeException re ) { 515 assertTrue(true); 516 } 517 } 518 519 520 521 /** 522 * try PT 523 */ 524 public void testPT() { 525 try { 526 new Duration("PT"); 527 fail("didn't catch missing fields after T"); 528 } 529 catch ( RuntimeException re ) { 530 assertTrue(true); 531 } 532 } 533 534 535 /** 536 * try PT5Y 537 */ 538 public void testPT5Y() { 539 try { 540 new Duration("PT5Y"); 541 fail("didn't catch year after T"); 542 } 543 catch ( RuntimeException re ) { 544 assertTrue(true); 545 } 546 } 547 548 549 /** 550 * try PT0.5H0.5M 551 */ 552 public void testPT0pt5H0pt5M() { 553 try { 554 new Duration("PT0.5H0.5M"); 555 fail("didn't catch multiple fraction after T"); 556 } 557 catch ( RuntimeException re ) { 558 assertTrue(true); 559 } 560 } 561 562 563 564 /** 565 * try PT5H3S19S 566 */ 567 public void testPT5H3S19S() { 568 try { 569 new Duration("PT5H3S19S"); 570 fail("didn't catch multiple seconds after hour"); 571 } 572 catch ( RuntimeException re ) { 573 assertTrue(true); 574 } 575 } 576 577 /** 578 * try PT7H2.5M 579 */ 580 public void testPT7H2pt5M() { 581 Duration d = new Duration("PT7H2.5M"); 582 assertEquals( 0, d.getYears() ); 583 assertEquals( 0, d.getMonths() ); 584 assertEquals( 0, d.getDays() ); 585 assertEquals( 7, d.getHours() ); 586 assertEquals( 2, d.getMinutes() ); 587 assertEquals( 0, d.getSeconds() ); 588 assertEquals( "5", d.getFraction() ); 589 assertEquals( Duration.MINUTE_FRACTION, d.getFractionUnit() ); 590 assertEquals( "PT7H2.5M", d.toString() ); 591 } 592 593 594 /** 595 * try PT2.5M 596 */ 597 public void testPT2pt5M() { 598 Duration d = new Duration("PT2.5M"); 599 assertEquals( 0, d.getYears() ); 600 assertEquals( 0, d.getMonths() ); 601 assertEquals( 0, d.getDays() ); 602 assertEquals( 0, d.getHours() ); 603 assertEquals( 2, d.getMinutes() ); 604 assertEquals( 0, d.getSeconds() ); 605 assertEquals( "5", d.getFraction() ); 606 assertEquals( Duration.MINUTE_FRACTION, d.getFractionUnit() ); 607 assertEquals( "PT2.5M", d.toString() ); 608 } 609 610 611 /** 612 * try PT7M2.5S 613 */ 614 public void testPT7M2pt5S() { 615 Duration d = new Duration("PT7M2.5S"); 616 assertEquals( 0, d.getYears() ); 617 assertEquals( 0, d.getMonths() ); 618 assertEquals( 0, d.getDays() ); 619 assertEquals( 0, d.getHours() ); 620 assertEquals( 7, d.getMinutes() ); 621 assertEquals( 2, d.getSeconds() ); 622 assertEquals( "5", d.getFraction() ); 623 assertEquals( Duration.SECOND_FRACTION, d.getFractionUnit() ); 624 assertEquals( "PT7M2.5S", d.toString() ); 625 } 626 627 /** 628 * try P5Y12M5D4H 629 */ 630 public void testP5Y12M5D4H() { 631 try { 632 new Duration("P5Y12M5D4H"); 633 fail("didn't catch year, month, day, hour sequence without T"); 634 } 635 catch ( RuntimeException re ) { 636 assertTrue(true); 637 } 638 } 639 640 /** 641 * try P1M2DT3H 642 */ 643 public void testP1M2DT3H() { 644 Duration d = new Duration("P1M2DT3H"); 645 assertEquals( 0, d.getYears() ); 646 assertEquals( 1, d.getMonths() ); 647 assertEquals( 2, d.getDays() ); 648 assertEquals( 3, d.getHours() ); 649 assertEquals( 0, d.getMinutes() ); 650 assertEquals( 0, d.getSeconds() ); 651 assertEquals( "P1M2DT3H", d.toString() ); 652 } 653 654 655 /** 656 * test PT5H3H 657 */ 658 public void testPT5H3H() { 659 try { 660 new Duration("PT5H3H"); 661 fail("didn't catch double hour"); 662 } 663 catch ( RuntimeException re ) { 664 assertTrue(true); 665 } 666 667 } 668 669 /** 670 * test PT5M3M 671 */ 672 public void testPT5M3M() { 673 try { 674 new Duration("PT5M3M"); 675 fail("didn't catch double minute"); 676 } 677 catch ( RuntimeException re ) { 678 assertTrue(true); 679 } 680 681 } 682 683 684 685 /** 686 * test PT5S3S 687 */ 688 public void testPT5S3S() { 689 try { 690 new Duration("PT5S3S"); 691 fail("didn't catch double second"); 692 } 693 catch ( RuntimeException re ) { 694 assertTrue(true); 695 } 696 697 } 698 699 /** 700 * test P1YfD 701 */ 702 public void testP1YfD() { 703 try { 704 new Duration("P1YfD"); 705 fail("didn't catch bad 2nd number before T"); 706 } 707 catch ( RuntimeException re ) { 708 assertTrue(true); 709 } 710 711 } 712 713 714 /** 715 * try PT4H3.5M4.8S 716 */ 717 public void testPT4H3pt5M4pt8S() { 718 try { 719 new Duration("PT4H3.5M4.8S"); 720 fail("didn't catch multiple fraction in 2nd & 3rd fields after T"); 721 } 722 catch ( RuntimeException re ) { 723 assertTrue(true); 724 } 725 } 726 727 728 729 /** 730 * try PT4H3M6q 731 */ 732 public void testPT4H3M6q() { 733 try { 734 new Duration("PT4H3M6q"); 735 fail("didn't catch bad unit in 3rd fields after T"); 736 } 737 catch ( RuntimeException re ) { 738 assertTrue(true); 739 } 740 } 741 742 743 744 /** 745 * try PT4H3M6S0 746 */ 747 public void testPT4H3M6S0() { 748 try { 749 new Duration("PT4H3M6S0"); 750 fail("didn't catch extra field after 3rd field after T"); 751 } 752 catch ( RuntimeException re ) { 753 assertTrue(true); 754 } 755 } 756 757 /** 758 * try PxYT5D 759 */ 760 public void testPxYT5D() { 761 try { 762 new Duration("PxYT5D"); 763 fail("didn't bad integer before T"); 764 } 765 catch ( RuntimeException re ) { 766 assertTrue(true); 767 } 768 } 769 770 /** 771 * test getting fraction 772 */ 773 public void testGetFraction() { 774 Duration d = new Duration(); 775 assertEquals( 0, d.getHours() ); 776 assertEquals( null, d.getFraction() ); 777 assertEquals( Duration.UNSET_FRACTION, d.getFractionUnit() ); 778 d = new Duration("PT1.25H"); 779 assertEquals( 1, d.getHours() ); 780 assertEquals( "25", d.getFraction() ); 781 assertEquals( Duration.HOUR_FRACTION, d.getFractionUnit() ); 782 } 783 784 /** number of milliseconds in a second */ 785 private final static int MS_IN_SEC = 1000; 786 787 /** number of seconds in a minute */ 788 private final static int SEC_IN_MIN = 60; 789 790 /** number of minutes in an hour */ 791 private final static int MIN_IN_HR = 60; 792 793 /** number of hours in a day */ 794 private final static int HR_IN_DAY = 24; 795 796 /** number of seconds in a day */ 797 private final static int SEC_IN_DAY = SEC_IN_MIN * MIN_IN_HR * HR_IN_DAY; 798 799 /** number of milliseconds in a day */ 800 private final static int MS_IN_DAY = MS_IN_SEC * SEC_IN_DAY; 801 802 /** number of days in week */ 803 private final static int DAY_IN_WK = 7; 804 805 /** number of days in month - ARBITRARY */ 806 private final static int DAY_IN_MON = 30; 807 808 /** number of days in year - ARBITRARY */ 809 private final static int DAY_IN_YR = 365; 810 811 /** 812 * test toSeconds() 813 */ 814 public void testToSeconds() { 815 Duration d; 816 817 d = new Duration(); 818 d.setWeeks( 5 ); 819 assertEquals( 5 * SEC_IN_DAY * DAY_IN_WK, d.toSeconds() ); 820 821 d = new Duration("PT0.5H"); 822 assertEquals( SEC_IN_MIN * MIN_IN_HR / 2, d.toSeconds() ); 823 824 d = new Duration("PT0.5M"); 825 assertEquals( SEC_IN_MIN / 2, d.toSeconds() ); 826 827 d = new Duration("PT0.5S"); 828 assertEquals( 0, d.toSeconds() ); 829 830 } 831 832 833 /** 834 * test toMilliseconds() 835 */ 836 public void testToMilliseconds() { 837 Duration d; 838 839 d = new Duration(); 840 d.setWeeks( 5 ); 841 assertEquals( 1000* 5 * SEC_IN_DAY * DAY_IN_WK, d.toMilliseconds() ); 842 843 d = new Duration("PT0.5H"); 844 assertEquals( 1000 * SEC_IN_MIN * MIN_IN_HR / 2, d.toMilliseconds() ); 845 846 d = new Duration("PT0.5M"); 847 assertEquals( 1000 * SEC_IN_MIN / 2, d.toMilliseconds() ); 848 849 d = new Duration("PT0.5S"); 850 assertEquals( 500, d.toMilliseconds() ); 851 852 } 853 854 855 // suite 856 857 /** 858 * return the implicit suite of tests 859 * @return the implicit suite of tests 860 */ 861 public static Test suite() { 862 return new TestSuite( 863 DurationTest.class, 864 JadeMXSuiteTest.nameWithClass( DurationTest.class, 865 "testing Duration: ISO 8601 duration class") ); 866 } 867 868 }