GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 5cefd1...492078 )
by Anton
04:08
created

AbstractString::ensureStart()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
nc 3
nop 1
dl 0
loc 17
c 1
b 0
f 0
cc 3
rs 9.9666
1
<?php
2
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Symfony\Component\String;
13
14
use Symfony\Component\String\Exception\ExceptionInterface;
15
use Symfony\Component\String\Exception\InvalidArgumentException;
16
use Symfony\Component\String\Exception\RuntimeException;
17
18
/**
19
 * Represents a string of abstract characters.
20
 *
21
 * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters).
22
 * This class is the abstract type to use as a type-hint when the logic you want to
23
 * implement doesn't care about the exact variant it deals with.
24
 *
25
 * @author Nicolas Grekas <[email protected]>
26
 * @author Hugo Hamon <[email protected]>
27
 *
28
 * @throws ExceptionInterface
29
 */
30
abstract class AbstractString implements \Stringable, \JsonSerializable
31
{
32
    public const PREG_PATTERN_ORDER = \PREG_PATTERN_ORDER;
33
    public const PREG_SET_ORDER = \PREG_SET_ORDER;
34
    public const PREG_OFFSET_CAPTURE = \PREG_OFFSET_CAPTURE;
35
    public const PREG_UNMATCHED_AS_NULL = \PREG_UNMATCHED_AS_NULL;
36
37
    public const PREG_SPLIT = 0;
38
    public const PREG_SPLIT_NO_EMPTY = \PREG_SPLIT_NO_EMPTY;
39
    public const PREG_SPLIT_DELIM_CAPTURE = \PREG_SPLIT_DELIM_CAPTURE;
40
    public const PREG_SPLIT_OFFSET_CAPTURE = \PREG_SPLIT_OFFSET_CAPTURE;
41
42
    protected $string = '';
43
    protected $ignoreCase = false;
44
45
    abstract public function __construct(string $string = '');
46
47
    /**
48
     * Unwraps instances of AbstractString back to strings.
49
     *
50
     * @return string[]|array
51
     */
52
    public static function unwrap(array $values): array
53
    {
54
        foreach ($values as $k => $v) {
55
            if ($v instanceof self) {
56
                $values[$k] = $v->__toString();
57
            } elseif (\is_array($v) && $values[$k] !== $v = static::unwrap($v)) {
58
                $values[$k] = $v;
59
            }
60
        }
61
62
        return $values;
63
    }
64
65
    /**
66
     * Wraps (and normalizes) strings in instances of AbstractString.
67
     *
68
     * @return static[]|array
69
     */
70
    public static function wrap(array $values): array
71
    {
72
        $i = 0;
73
        $keys = null;
74
75
        foreach ($values as $k => $v) {
76
            if (\is_string($k) && '' !== $k && $k !== $j = (string) new static($k)) {
77
                $keys = $keys ?? array_keys($values);
78
                $keys[$i] = $j;
79
            }
80
81
            if (\is_string($v)) {
82
                $values[$k] = new static($v);
83
            } elseif (\is_array($v) && $values[$k] !== $v = static::wrap($v)) {
84
                $values[$k] = $v;
85
            }
86
87
            ++$i;
88
        }
89
90
        return null !== $keys ? array_combine($keys, $values) : $values;
91
    }
92
93
    /**
94
     * @param string|string[] $needle
95
     *
96
     * @return static
97
     */
98
    public function after($needle, bool $includeNeedle = false, int $offset = 0): self
99
    {
100
        $str = clone $this;
101
        $i = \PHP_INT_MAX;
102
103
        foreach ((array) $needle as $n) {
104
            $n = (string) $n;
105
            $j = $this->indexOf($n, $offset);
106
107
            if (null !== $j && $j < $i) {
108
                $i = $j;
109
                $str->string = $n;
110
            }
111
        }
112
113
        if (\PHP_INT_MAX === $i) {
114
            return $str;
115
        }
116
117
        if (!$includeNeedle) {
118
            $i += $str->length();
119
        }
120
121
        return $this->slice($i);
122
    }
123
124
    /**
125
     * @param string|string[] $needle
126
     *
127
     * @return static
128
     */
129
    public function afterLast($needle, bool $includeNeedle = false, int $offset = 0): self
130
    {
131
        $str = clone $this;
132
        $i = null;
133
134
        foreach ((array) $needle as $n) {
135
            $n = (string) $n;
136
            $j = $this->indexOfLast($n, $offset);
137
138
            if (null !== $j && $j >= $i) {
139
                $i = $offset = $j;
140
                $str->string = $n;
141
            }
142
        }
143
144
        if (null === $i) {
145
            return $str;
146
        }
147
148
        if (!$includeNeedle) {
149
            $i += $str->length();
150
        }
151
152
        return $this->slice($i);
153
    }
154
155
    /**
156
     * @return static
157
     */
158
    abstract public function append(string ...$suffix): self;
159
160
    /**
161
     * @param string|string[] $needle
162
     *
163
     * @return static
164
     */
165
    public function before($needle, bool $includeNeedle = false, int $offset = 0): self
166
    {
167
        $str = clone $this;
168
        $i = \PHP_INT_MAX;
169
170
        foreach ((array) $needle as $n) {
171
            $n = (string) $n;
172
            $j = $this->indexOf($n, $offset);
173
174
            if (null !== $j && $j < $i) {
175
                $i = $j;
176
                $str->string = $n;
177
            }
178
        }
179
180
        if (\PHP_INT_MAX === $i) {
181
            return $str;
182
        }
183
184
        if ($includeNeedle) {
185
            $i += $str->length();
186
        }
187
188
        return $this->slice(0, $i);
189
    }
190
191
    /**
192
     * @param string|string[] $needle
193
     *
194
     * @return static
195
     */
196
    public function beforeLast($needle, bool $includeNeedle = false, int $offset = 0): self
197
    {
198
        $str = clone $this;
199
        $i = null;
200
201
        foreach ((array) $needle as $n) {
202
            $n = (string) $n;
203
            $j = $this->indexOfLast($n, $offset);
204
205
            if (null !== $j && $j >= $i) {
206
                $i = $offset = $j;
207
                $str->string = $n;
208
            }
209
        }
210
211
        if (null === $i) {
212
            return $str;
213
        }
214
215
        if ($includeNeedle) {
216
            $i += $str->length();
217
        }
218
219
        return $this->slice(0, $i);
220
    }
221
222
    /**
223
     * @return int[]
224
     */
225
    public function bytesAt(int $offset): array
226
    {
227
        $str = $this->slice($offset, 1);
228
229
        return '' === $str->string ? [] : array_values(unpack('C*', $str->string));
230
    }
231
232
    /**
233
     * @return static
234
     */
235
    abstract public function camel(): self;
236
237
    /**
238
     * @return static[]
239
     */
240
    abstract public function chunk(int $length = 1): array;
241
242
    /**
243
     * @return static
244
     */
245
    public function collapseWhitespace(): self
246
    {
247
        $str = clone $this;
248
        $str->string = trim(preg_replace('/(?:\s{2,}+|[^\S ])/', ' ', $str->string));
249
250
        return $str;
251
    }
252
253
    /**
254
     * @param string|string[] $needle
255
     */
256
    public function containsAny($needle): bool
257
    {
258
        return null !== $this->indexOf($needle);
259
    }
260
261
    /**
262
     * @param string|string[] $suffix
263
     */
264
    public function endsWith($suffix): bool
265
    {
266
        if (!\is_array($suffix) && !$suffix instanceof \Traversable) {
267
            throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
268
        }
269
270
        foreach ($suffix as $s) {
271
            if ($this->endsWith((string) $s)) {
272
                return true;
273
            }
274
        }
275
276
        return false;
277
    }
278
279
    /**
280
     * @return static
281
     */
282
    public function ensureEnd(string $suffix): self
283
    {
284
        if (!$this->endsWith($suffix)) {
285
            return $this->append($suffix);
286
        }
287
288
        $suffix = preg_quote($suffix);
289
        $regex = '{('.$suffix.')(?:'.$suffix.')++$}D';
290
291
        return $this->replaceMatches($regex.($this->ignoreCase ? 'i' : ''), '$1');
292
    }
293
294
    /**
295
     * @return static
296
     */
297
    public function ensureStart(string $prefix): self
298
    {
299
        $prefix = new static($prefix);
300
301
        if (!$this->startsWith($prefix)) {
302
            return $this->prepend($prefix);
0 ignored issues
show
Bug introduced by
$prefix of type iterable is incompatible with the type string expected by parameter $prefix of Symfony\Component\String\AbstractString::prepend(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

302
            return $this->prepend(/** @scrutinizer ignore-type */ $prefix);
Loading history...
303
        }
304
305
        $str = clone $this;
306
        $i = $prefixLen = $prefix->length();
307
308
        while ($this->indexOf($prefix, $i) === $i) {
309
            $str = $str->slice($prefixLen);
310
            $i += $prefixLen;
311
        }
312
313
        return $str;
314
    }
315
316
    /**
317
     * @param string|string[] $string
318
     */
319
    public function equalsTo($string): bool
320
    {
321
        if (!\is_array($string) && !$string instanceof \Traversable) {
322
            throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
323
        }
324
325
        foreach ($string as $s) {
326
            if ($this->equalsTo((string) $s)) {
327
                return true;
328
            }
329
        }
330
331
        return false;
332
    }
333
334
    /**
335
     * @return static
336
     */
337
    abstract public function folded(): self;
338
339
    /**
340
     * @return static
341
     */
342
    public function ignoreCase(): self
343
    {
344
        $str = clone $this;
345
        $str->ignoreCase = true;
346
347
        return $str;
348
    }
349
350
    /**
351
     * @param string|string[] $needle
352
     */
353
    public function indexOf($needle, int $offset = 0): ?int
354
    {
355
        if (!\is_array($needle) && !$needle instanceof \Traversable) {
356
            throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
357
        }
358
359
        $i = \PHP_INT_MAX;
360
361
        foreach ($needle as $n) {
362
            $j = $this->indexOf((string) $n, $offset);
363
364
            if (null !== $j && $j < $i) {
365
                $i = $j;
366
            }
367
        }
368
369
        return \PHP_INT_MAX === $i ? null : $i;
370
    }
371
372
    /**
373
     * @param string|string[] $needle
374
     */
375
    public function indexOfLast($needle, int $offset = 0): ?int
376
    {
377
        if (!\is_array($needle) && !$needle instanceof \Traversable) {
378
            throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
379
        }
380
381
        $i = null;
382
383
        foreach ($needle as $n) {
384
            $j = $this->indexOfLast((string) $n, $offset);
385
386
            if (null !== $j && $j >= $i) {
387
                $i = $offset = $j;
388
            }
389
        }
390
391
        return $i;
392
    }
393
394
    public function isEmpty(): bool
395
    {
396
        return '' === $this->string;
397
    }
398
399
    /**
400
     * @return static
401
     */
402
    abstract public function join(array $strings, string $lastGlue = null): self;
403
404
    public function jsonSerialize(): string
405
    {
406
        return $this->string;
407
    }
408
409
    abstract public function length(): int;
410
411
    /**
412
     * @return static
413
     */
414
    abstract public function lower(): self;
415
416
    /**
417
     * Matches the string using a regular expression.
418
     *
419
     * Pass PREG_PATTERN_ORDER or PREG_SET_ORDER as $flags to get all occurrences matching the regular expression.
420
     *
421
     * @return array All matches in a multi-dimensional array ordered according to flags
422
     */
423
    abstract public function match(string $regexp, int $flags = 0, int $offset = 0): array;
424
425
    /**
426
     * @return static
427
     */
428
    abstract public function padBoth(int $length, string $padStr = ' '): self;
429
430
    /**
431
     * @return static
432
     */
433
    abstract public function padEnd(int $length, string $padStr = ' '): self;
434
435
    /**
436
     * @return static
437
     */
438
    abstract public function padStart(int $length, string $padStr = ' '): self;
439
440
    /**
441
     * @return static
442
     */
443
    abstract public function prepend(string ...$prefix): self;
444
445
    /**
446
     * @return static
447
     */
448
    public function repeat(int $multiplier): self
449
    {
450
        if (0 > $multiplier) {
451
            throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier));
452
        }
453
454
        $str = clone $this;
455
        $str->string = str_repeat($str->string, $multiplier);
456
457
        return $str;
458
    }
459
460
    /**
461
     * @return static
462
     */
463
    abstract public function replace(string $from, string $to): self;
464
465
    /**
466
     * @param string|callable $to
467
     *
468
     * @return static
469
     */
470
    abstract public function replaceMatches(string $fromRegexp, $to): self;
471
472
    /**
473
     * @return static
474
     */
475
    abstract public function reverse(): self;
476
477
    /**
478
     * @return static
479
     */
480
    abstract public function slice(int $start = 0, int $length = null): self;
481
482
    /**
483
     * @return static
484
     */
485
    abstract public function snake(): self;
486
487
    /**
488
     * @return static
489
     */
490
    abstract public function splice(string $replacement, int $start = 0, int $length = null): self;
491
492
    /**
493
     * @return static[]
494
     */
495
    public function split(string $delimiter, int $limit = null, int $flags = null): array
496
    {
497
        if (null === $flags) {
498
            throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.');
499
        }
500
501
        if ($this->ignoreCase) {
502
            $delimiter .= 'i';
503
        }
504
505
        set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
506
507
        try {
508
            if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) {
509
                $lastError = preg_last_error();
510
511
                foreach (get_defined_constants(true)['pcre'] as $k => $v) {
512
                    if ($lastError === $v && '_ERROR' === substr($k, -6)) {
513
                        throw new RuntimeException('Splitting failed with '.$k.'.');
514
                    }
515
                }
516
517
                throw new RuntimeException('Splitting failed with unknown error code.');
518
            }
519
        } finally {
520
            restore_error_handler();
521
        }
522
523
        $str = clone $this;
524
525
        if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) {
526
            foreach ($chunks as &$chunk) {
527
                $str->string = $chunk[0];
528
                $chunk[0] = clone $str;
529
            }
530
        } else {
531
            foreach ($chunks as &$chunk) {
532
                $str->string = $chunk;
533
                $chunk = clone $str;
534
            }
535
        }
536
537
        return $chunks;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $chunks returns the type array<mixed,array|string> which is incompatible with the documented return type array<mixed,Symfony\Comp...\String\AbstractString>.
Loading history...
538
    }
539
540
    /**
541
     * @param string|string[] $prefix
542
     */
543
    public function startsWith($prefix): bool
544
    {
545
        if (!\is_array($prefix) && !$prefix instanceof \Traversable) {
546
            throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class));
547
        }
548
549
        foreach ($prefix as $prefix) {
0 ignored issues
show
introduced by
$prefix is overwriting one of the parameters of this function.
Loading history...
550
            if ($this->startsWith((string) $prefix)) {
551
                return true;
552
            }
553
        }
554
555
        return false;
556
    }
557
558
    /**
559
     * @return static
560
     */
561
    abstract public function title(bool $allWords = false): self;
562
563
    public function toByteString(string $toEncoding = null): ByteString
564
    {
565
        $b = new ByteString();
566
567
        $toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], true) ? 'UTF-8' : $toEncoding;
568
569
        if (null === $toEncoding || $toEncoding === $fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252') {
0 ignored issues
show
Unused Code introduced by
The assignment to $fromEncoding is dead and can be removed.
Loading history...
570
            $b->string = $this->string;
571
572
            return $b;
573
        }
574
575
        set_error_handler(static function ($t, $m) { throw new InvalidArgumentException($m); });
576
577
        try {
578
            try {
579
                $b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8');
580
            } catch (InvalidArgumentException $e) {
581
                if (!\function_exists('iconv')) {
582
                    throw $e;
583
                }
584
585
                $b->string = iconv('UTF-8', $toEncoding, $this->string);
586
            }
587
        } finally {
588
            restore_error_handler();
589
        }
590
591
        return $b;
592
    }
593
594
    public function toCodePointString(): CodePointString
595
    {
596
        return new CodePointString($this->string);
597
    }
598
599
    public function toString(): string
600
    {
601
        return $this->string;
602
    }
603
604
    public function toUnicodeString(): UnicodeString
605
    {
606
        return new UnicodeString($this->string);
607
    }
608
609
    /**
610
     * @return static
611
     */
612
    abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
613
614
    /**
615
     * @return static
616
     */
617
    abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
618
619
    /**
620
     * @param string|string[] $prefix
621
     *
622
     * @return static
623
     */
624
    public function trimPrefix($prefix): self
625
    {
626
        if (\is_array($prefix) || $prefix instanceof \Traversable) {
627
            foreach ($prefix as $s) {
628
                $t = $this->trimPrefix($s);
629
630
                if ($t->string !== $this->string) {
631
                    return $t;
632
                }
633
            }
634
635
            return clone $this;
636
        }
637
638
        $str = clone $this;
639
640
        if ($prefix instanceof self) {
0 ignored issues
show
introduced by
$prefix is never a sub-type of self.
Loading history...
641
            $prefix = $prefix->string;
642
        } else {
643
            $prefix = (string) $prefix;
644
        }
645
646
        if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) {
0 ignored issues
show
Bug introduced by
It seems like $this->ignoreCase can also be of type null; however, parameter $case_insensitive of substr_compare() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

646
        if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), /** @scrutinizer ignore-type */ $this->ignoreCase)) {
Loading history...
647
            $str->string = substr($this->string, \strlen($prefix));
648
        }
649
650
        return $str;
651
    }
652
653
    /**
654
     * @return static
655
     */
656
    abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self;
657
658
    /**
659
     * @param string|string[] $suffix
660
     *
661
     * @return static
662
     */
663
    public function trimSuffix($suffix): self
664
    {
665
        if (\is_array($suffix) || $suffix instanceof \Traversable) {
666
            foreach ($suffix as $s) {
667
                $t = $this->trimSuffix($s);
668
669
                if ($t->string !== $this->string) {
670
                    return $t;
671
                }
672
            }
673
674
            return clone $this;
675
        }
676
677
        $str = clone $this;
678
679
        if ($suffix instanceof self) {
0 ignored issues
show
introduced by
$suffix is never a sub-type of self.
Loading history...
680
            $suffix = $suffix->string;
681
        } else {
682
            $suffix = (string) $suffix;
683
        }
684
685
        if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) {
0 ignored issues
show
Bug introduced by
It seems like $this->ignoreCase can also be of type null; however, parameter $case_insensitive of substr_compare() does only seem to accept boolean, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

685
        if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, /** @scrutinizer ignore-type */ $this->ignoreCase)) {
Loading history...
686
            $str->string = substr($this->string, 0, -\strlen($suffix));
687
        }
688
689
        return $str;
690
    }
691
692
    /**
693
     * @return static
694
     */
695
    public function truncate(int $length, string $ellipsis = '', bool $cut = true): self
696
    {
697
        $stringLength = $this->length();
698
699
        if ($stringLength <= $length) {
700
            return clone $this;
701
        }
702
703
        $ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0;
704
705
        if ($length < $ellipsisLength) {
706
            $ellipsisLength = 0;
707
        }
708
709
        if (!$cut) {
710
            if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) {
711
                return clone $this;
712
            }
713
714
            $length += $ellipsisLength;
715
        }
716
717
        $str = $this->slice(0, $length - $ellipsisLength);
718
719
        return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str;
720
    }
721
722
    /**
723
     * @return static
724
     */
725
    abstract public function upper(): self;
726
727
    /**
728
     * Returns the printable length on a terminal.
729
     */
730
    abstract public function width(bool $ignoreAnsiDecoration = true): int;
731
732
    /**
733
     * @return static
734
     */
735
    public function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): self
736
    {
737
        $lines = '' !== $break ? $this->split($break) : [clone $this];
738
        $chars = [];
739
        $mask = '';
740
741
        if (1 === \count($lines) && '' === $lines[0]->string) {
742
            return $lines[0];
743
        }
744
745
        foreach ($lines as $i => $line) {
746
            if ($i) {
747
                $chars[] = $break;
748
                $mask .= '#';
749
            }
750
751
            foreach ($line->chunk() as $char) {
752
                $chars[] = $char->string;
753
                $mask .= ' ' === $char->string ? ' ' : '?';
754
            }
755
        }
756
757
        $string = '';
758
        $j = 0;
759
        $b = $i = -1;
760
        $mask = wordwrap($mask, $width, '#', $cut);
761
762
        while (false !== $b = strpos($mask, '#', $b + 1)) {
763
            for (++$i; $i < $b; ++$i) {
764
                $string .= $chars[$j];
765
                unset($chars[$j++]);
766
            }
767
768
            if ($break === $chars[$j] || ' ' === $chars[$j]) {
769
                unset($chars[$j++]);
770
            }
771
772
            $string .= $break;
773
        }
774
775
        $str = clone $this;
776
        $str->string = $string.implode('', $chars);
777
778
        return $str;
779
    }
780
781
    public function __sleep(): array
782
    {
783
        return ['string'];
784
    }
785
786
    public function __clone()
787
    {
788
        $this->ignoreCase = false;
789
    }
790
791
    public function __toString(): string
792
    {
793
        return $this->string;
794
    }
795
}
796