001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3;
018
019import java.util.Objects;
020
021/**
022 * Operations on char primitives and Character objects.
023 *
024 * <p>
025 * This class tries to handle {@code null} input gracefully.
026 * An exception will not be thrown for a {@code null} input.
027 * Each method documents its behavior in more detail.
028 * </p>
029 *
030 * <p>
031 * #ThreadSafe#
032 * </p>
033 *
034 * @since 2.1
035 */
036public class CharUtils {
037
038    private static final String[] CHAR_STRING_ARRAY = ArrayUtils.setAll(new String[128], i -> String.valueOf((char) i));
039
040    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
041
042    /**
043     * Linefeed character LF ({@code '\n'}, Unicode 000a).
044     *
045     * @see <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.6">JLF: Escape Sequences
046     *      for Character and String Literals</a>
047     * @since 2.2
048     */
049    public static final char LF = '\n';
050
051    /**
052     * Carriage return character CR ('\r', Unicode 000d).
053     *
054     * @see <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.6">JLF: Escape Sequences
055     *      for Character and String Literals</a>
056     * @since 2.2
057     */
058    public static final char CR = '\r';
059
060    /**
061     * {@code \u0000} null control character ('\0'), abbreviated NUL.
062     *
063     * @since 3.6
064     */
065    public static final char NUL = '\0';
066
067    /**
068     * Compares two {@code char} values numerically. This is the same functionality as provided in Java 7.
069     *
070     * @param x The first {@code char} to compare
071     * @param y The second {@code char} to compare
072     * @return The value {@code 0} if {@code x == y};
073     *         a value less than {@code 0} if {@code x < y}; and
074     *         a value greater than {@code 0} if {@code x > y}
075     * @since 3.4
076     */
077    public static int compare(final char x, final char y) {
078        return x - y;
079    }
080
081    /**
082     * Tests whether the character is ASCII 7 bit.
083     *
084     * <pre>
085     *   CharUtils.isAscii('a')  = true
086     *   CharUtils.isAscii('A')  = true
087     *   CharUtils.isAscii('3')  = true
088     *   CharUtils.isAscii('-')  = true
089     *   CharUtils.isAscii('\n') = true
090     *   CharUtils.isAscii('&copy;') = false
091     * </pre>
092     *
093     * @param ch  The character to check
094     * @return true if less than 128
095     */
096    public static boolean isAscii(final char ch) {
097        return ch < 128;
098    }
099
100    /**
101     * Tests whether the character is ASCII 7 bit alphabetic.
102     *
103     * <pre>
104     *   CharUtils.isAsciiAlpha('a')  = true
105     *   CharUtils.isAsciiAlpha('A')  = true
106     *   CharUtils.isAsciiAlpha('3')  = false
107     *   CharUtils.isAsciiAlpha('-')  = false
108     *   CharUtils.isAsciiAlpha('\n') = false
109     *   CharUtils.isAsciiAlpha('&copy;') = false
110     * </pre>
111     *
112     * @param ch  The character to check
113     * @return true if between 65 and 90 or 97 and 122 inclusive
114     */
115    public static boolean isAsciiAlpha(final char ch) {
116        return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch);
117    }
118
119    /**
120     * Tests whether the character is ASCII 7 bit alphabetic lower case.
121     *
122     * <pre>
123     *   CharUtils.isAsciiAlphaLower('a')  = true
124     *   CharUtils.isAsciiAlphaLower('A')  = false
125     *   CharUtils.isAsciiAlphaLower('3')  = false
126     *   CharUtils.isAsciiAlphaLower('-')  = false
127     *   CharUtils.isAsciiAlphaLower('\n') = false
128     *   CharUtils.isAsciiAlphaLower('&copy;') = false
129     * </pre>
130     *
131     * @param ch  The character to check
132     * @return true if between 97 and 122 inclusive
133     */
134    public static boolean isAsciiAlphaLower(final char ch) {
135        return ch >= 'a' && ch <= 'z';
136    }
137
138    /**
139     * Tests whether the character is ASCII 7 bit alphanumeric character.
140     *
141     * <pre>
142     *   CharUtils.isAsciiAlphanumeric('a')  = true
143     *   CharUtils.isAsciiAlphanumeric('A')  = true
144     *   CharUtils.isAsciiAlphanumeric('3')  = true
145     *   CharUtils.isAsciiAlphanumeric('-')  = false
146     *   CharUtils.isAsciiAlphanumeric('\n') = false
147     *   CharUtils.isAsciiAlphanumeric('&copy;') = false
148     * </pre>
149     *
150     * @param ch  The character to check
151     * @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive
152     */
153    public static boolean isAsciiAlphanumeric(final char ch) {
154        return isAsciiAlpha(ch) || isAsciiNumeric(ch);
155    }
156
157    /**
158     * Tests whether the character is ASCII 7 bit alphabetic upper case.
159     *
160     * <pre>
161     *   CharUtils.isAsciiAlphaUpper('a')  = false
162     *   CharUtils.isAsciiAlphaUpper('A')  = true
163     *   CharUtils.isAsciiAlphaUpper('3')  = false
164     *   CharUtils.isAsciiAlphaUpper('-')  = false
165     *   CharUtils.isAsciiAlphaUpper('\n') = false
166     *   CharUtils.isAsciiAlphaUpper('&copy;') = false
167     * </pre>
168     *
169     * @param ch  The character to check
170     * @return true if between 65 and 90 inclusive
171     */
172    public static boolean isAsciiAlphaUpper(final char ch) {
173        return ch >= 'A' && ch <= 'Z';
174    }
175
176    /**
177     * Tests whether the character is ASCII 7 bit control.
178     *
179     * <pre>
180     *   CharUtils.isAsciiControl('a')  = false
181     *   CharUtils.isAsciiControl('A')  = false
182     *   CharUtils.isAsciiControl('3')  = false
183     *   CharUtils.isAsciiControl('-')  = false
184     *   CharUtils.isAsciiControl('\n') = true
185     *   CharUtils.isAsciiControl('&copy;') = false
186     * </pre>
187     *
188     * @param ch  The character to check
189     * @return true if less than 32 or equals 127
190     */
191    public static boolean isAsciiControl(final char ch) {
192        return ch < 32 || ch == 127;
193    }
194
195    /**
196     * Tests whether the character is ASCII 7 bit numeric.
197     *
198     * <pre>
199     *   CharUtils.isAsciiNumeric('a')  = false
200     *   CharUtils.isAsciiNumeric('A')  = false
201     *   CharUtils.isAsciiNumeric('3')  = true
202     *   CharUtils.isAsciiNumeric('-')  = false
203     *   CharUtils.isAsciiNumeric('\n') = false
204     *   CharUtils.isAsciiNumeric('&copy;') = false
205     * </pre>
206     *
207     * @param ch  The character to check
208     * @return true if between 48 and 57 inclusive
209     */
210    public static boolean isAsciiNumeric(final char ch) {
211        return ch >= '0' && ch <= '9';
212    }
213
214    /**
215     * Tests whether the character is ASCII 7 bit numeric.
216     *
217     * <pre>
218     *   CharUtils.isAsciiNumeric('a')  = false
219     *   CharUtils.isAsciiNumeric('A')  = false
220     *   CharUtils.isAsciiNumeric('3')  = true
221     *   CharUtils.isAsciiNumeric('-')  = false
222     *   CharUtils.isAsciiNumeric('\n') = false
223     *   CharUtils.isAsciiNumeric('&copy;') = false
224     * </pre>
225     *
226     * @param ch  The code point to check.
227     * @return true if between 48 and 57 inclusive.
228     * @since 3.21.0
229     */
230    public static boolean isAsciiNumeric(final int ch) {
231        return ch >= '0' && ch <= '9';
232    }
233
234    /**
235     * Tests whether the character is ASCII 7 bit printable.
236     *
237     * <pre>
238     *   CharUtils.isAsciiPrintable('a')  = true
239     *   CharUtils.isAsciiPrintable('A')  = true
240     *   CharUtils.isAsciiPrintable('3')  = true
241     *   CharUtils.isAsciiPrintable('-')  = true
242     *   CharUtils.isAsciiPrintable('\n') = false
243     *   CharUtils.isAsciiPrintable('&copy;') = false
244     * </pre>
245     *
246     * @param ch  The character to check
247     * @return true if between 32 and 126 inclusive
248     */
249    public static boolean isAsciiPrintable(final char ch) {
250        return ch >= 32 && ch < 127;
251    }
252
253    /**
254     * Tests whether a character is a hexadecimal character.
255     *
256     * <pre>
257     *   CharUtils.isHex('0')  = true
258     *   CharUtils.isHex('3')  = true
259     *   CharUtils.isHex('9')  = true
260     *   CharUtils.isHex('a')  = true
261     *   CharUtils.isHex('f')  = true
262     *   CharUtils.isHex('g')  = false
263     *   CharUtils.isHex('A')  = true
264     *   CharUtils.isHex('F')  = true
265     *   CharUtils.isHex('G')  = false
266     *   CharUtils.isHex('#')  = false
267     *   CharUtils.isHex('-')  = false
268     *   CharUtils.isHex('\n') = false
269     *   CharUtils.isHex('&copy;') = false
270     * </pre>
271     *
272     * @param ch  The character to test.
273     * @return true if character is a hexadecimal character.
274     * @since 3.18.0
275     */
276    public static boolean isHex(final char ch) {
277        return isAsciiNumeric(ch) || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F';
278    }
279
280    /**
281     * Tests whether a character is a hexadecimal character.
282     *
283     * <pre>
284     *   CharUtils.isHex('0')  = true
285     *   CharUtils.isHex('3')  = true
286     *   CharUtils.isHex('9')  = true
287     *   CharUtils.isHex('a')  = true
288     *   CharUtils.isHex('f')  = true
289     *   CharUtils.isHex('g')  = false
290     *   CharUtils.isHex('A')  = true
291     *   CharUtils.isHex('F')  = true
292     *   CharUtils.isHex('G')  = false
293     *   CharUtils.isHex('#')  = false
294     *   CharUtils.isHex('-')  = false
295     *   CharUtils.isHex('\n') = false
296     *   CharUtils.isHex('&copy;') = false
297     * </pre>
298     *
299     * @param ch  The code point to test.
300     * @return true if character is a hexadecimal character.
301     * @since 3.21.0
302     */
303    public static boolean isHex(final int ch) {
304        return isAsciiNumeric(ch) || ch >= 'a' && ch <= 'f' || ch >= 'A' && ch <= 'F';
305    }
306
307    /**
308     * Tests if the given char is an octal digit. Octal digits are the character representations of the digits 0 to 7.
309     *
310     * @param ch The byte to check.
311     * @return true if the given char is the character representation of one of the digits from 0 to 7.
312     * @since 3.21.0
313     */
314    public static boolean isOctal(final byte ch) {
315        return ch >= '0' && ch <= '7';
316    }
317
318    /**
319     * Tests if the given char is an octal digit. Octal digits are the character representations of the digits 0 to 7.
320     *
321     * @param ch The char to check.
322     * @return true if the given char is the character representation of one of the digits from 0 to 7.
323     * @since 3.18.0
324     */
325    public static boolean isOctal(final char ch) {
326        return ch >= '0' && ch <= '7';
327    }
328
329    /**
330     * Converts the Character to a char throwing an exception for {@code null}.
331     *
332     * <pre>
333     *   CharUtils.toChar(' ')  = ' '
334     *   CharUtils.toChar('A')  = 'A'
335     *   CharUtils.toChar(null) throws NullPointerException
336     * </pre>
337     *
338     * @param ch  The character to convert
339     * @return The char value of the Character
340     * @throws NullPointerException Thrown if the Character is null.
341     */
342    public static char toChar(final Character ch) {
343        return Objects.requireNonNull(ch, "ch").charValue();
344    }
345
346    /**
347     * Converts the Character to a char handling {@code null}.
348     *
349     * <pre>
350     *   CharUtils.toChar(null, 'X') = 'X'
351     *   CharUtils.toChar(' ', 'X')  = ' '
352     *   CharUtils.toChar('A', 'X')  = 'A'
353     * </pre>
354     *
355     * @param ch  The character to convert
356     * @param defaultValue  The value to use if the  Character is null
357     * @return The char value of the Character or the default if null
358     */
359    public static char toChar(final Character ch, final char defaultValue) {
360        return ch != null ? ch.charValue() : defaultValue;
361    }
362
363    /**
364     * Converts the String to a char using the first character, throwing
365     * an exception on empty Strings.
366     *
367     * <pre>
368     *   CharUtils.toChar("A")  = 'A'
369     *   CharUtils.toChar("BA") = 'B'
370     *   CharUtils.toChar(null) throws NullPointerException
371     *   CharUtils.toChar("")   throws IllegalArgumentException
372     * </pre>
373     *
374     * @param str  The character to convert
375     * @return The char value of the first letter of the String
376     * @throws NullPointerException Thrown if the string is null.
377     * @throws IllegalArgumentException Thrown if the String is empty.
378     */
379    public static char toChar(final String str) {
380        Validate.notEmpty(str, "The String must not be empty");
381        return str.charAt(0);
382    }
383
384    /**
385     * Converts the String to a char using the first character, defaulting
386     * the value on empty Strings.
387     *
388     * <pre>
389     *   CharUtils.toChar(null, 'X') = 'X'
390     *   CharUtils.toChar("", 'X')   = 'X'
391     *   CharUtils.toChar("A", 'X')  = 'A'
392     *   CharUtils.toChar("BA", 'X') = 'B'
393     * </pre>
394     *
395     * @param str  The character to convert
396     * @param defaultValue  The value to use if the  Character is null
397     * @return The char value of the first letter of the String or the default if null
398     */
399    public static char toChar(final String str, final char defaultValue) {
400        return StringUtils.isEmpty(str) ? defaultValue : str.charAt(0);
401    }
402
403    /**
404     * Delegates to {@link Character#valueOf(char)}.
405     *
406     * @param c The character to convert
407     * @return A {@code Character} representing {@code c}.
408     * @deprecated Use {@link Character#valueOf(char)}.
409     */
410    @Deprecated
411    public static Character toCharacterObject(final char c) {
412        return Character.valueOf(c);
413    }
414
415    /**
416     * Converts the String to a Character using the first character, returning
417     * null for empty Strings.
418     *
419     * <p>
420     * For ASCII 7 bit characters, this uses a cache that will return the
421     * same Character object each time.
422     * </p>
423     *
424     * <pre>
425     *   CharUtils.toCharacterObject(null) = null
426     *   CharUtils.toCharacterObject("")   = null
427     *   CharUtils.toCharacterObject("A")  = 'A'
428     *   CharUtils.toCharacterObject("BA") = 'B'
429     * </pre>
430     *
431     * @param str  The character to convert
432     * @return The Character value of the first letter of the String
433     */
434    public static Character toCharacterObject(final String str) {
435        return StringUtils.isEmpty(str) ? null : Character.valueOf(str.charAt(0));
436    }
437
438    /**
439     * Converts the character to the Integer it represents, throwing an
440     * exception if the character is not numeric.
441     *
442     * <p>
443     * This method converts the char '1' to the int 1 and so on.
444     * </p>
445     *
446     * <pre>
447     *   CharUtils.toIntValue('3')  = 3
448     *   CharUtils.toIntValue('A')  throws IllegalArgumentException
449     * </pre>
450     *
451     * @param ch  The character to convert
452     * @return The int value of the character
453     * @throws IllegalArgumentException Thrown if the character is not ASCII numeric.
454     */
455    public static int toIntValue(final char ch) {
456        if (!isAsciiNumeric(ch)) {
457            throw new IllegalArgumentException("The character " + ch + " is not in the range '0' - '9'");
458        }
459        return ch - 48;
460    }
461
462    /**
463     * Converts the character to the Integer it represents, throwing an
464     * exception if the character is not numeric.
465     *
466     * <p>
467     * This method converts the char '1' to the int 1 and so on.
468     * </p>
469     *
470     * <pre>
471     *   CharUtils.toIntValue('3', -1)  = 3
472     *   CharUtils.toIntValue('A', -1)  = -1
473     * </pre>
474     *
475     * @param ch  The character to convert
476     * @param defaultValue  The default value to use if the character is not numeric
477     * @return The int value of the character
478     */
479    public static int toIntValue(final char ch, final int defaultValue) {
480        return isAsciiNumeric(ch) ? ch - 48 : defaultValue;
481    }
482
483    /**
484     * Converts the character to the Integer it represents, throwing an
485     * exception if the character is not numeric.
486     *
487     * <p>
488     * This method converts the char '1' to the int 1 and so on.
489     * </p>
490     *
491     * <pre>
492     *   CharUtils.toIntValue('3')  = 3
493     *   CharUtils.toIntValue(null) throws NullPointerException
494     *   CharUtils.toIntValue('A')  throws IllegalArgumentException
495     * </pre>
496     *
497     * @param ch  The character to convert, not null
498     * @return The int value of the character
499     * @throws NullPointerException Thrown if the Character is null.
500     * @throws IllegalArgumentException Thrown if the Character is not ASCII numeric.
501     */
502    public static int toIntValue(final Character ch) {
503        return toIntValue(toChar(ch));
504    }
505
506    /**
507     * Converts the character to the Integer it represents, throwing an
508     * exception if the character is not numeric.
509     *
510     * <p>
511     * This method converts the char '1' to the int 1 and so on.
512     * </p>
513     *
514     * <pre>
515     *   CharUtils.toIntValue(null, -1) = -1
516     *   CharUtils.toIntValue('3', -1)  = 3
517     *   CharUtils.toIntValue('A', -1)  = -1
518     * </pre>
519     *
520     * @param ch  The character to convert
521     * @param defaultValue  The default value to use if the character is not numeric
522     * @return The int value of the character
523     */
524    public static int toIntValue(final Character ch, final int defaultValue) {
525        return ch != null ? toIntValue(ch.charValue(), defaultValue) : defaultValue;
526    }
527
528    /**
529     * Converts the character to a String that contains the one character.
530     *
531     * <p>
532     * For ASCII 7 bit characters, this uses a cache that will return the
533     * same String object each time.
534     * </p>
535     *
536     * <pre>
537     *   CharUtils.toString(' ')  = " "
538     *   CharUtils.toString('A')  = "A"
539     * </pre>
540     *
541     * @param ch  The character to convert
542     * @return A String containing the one specified character
543     */
544    public static String toString(final char ch) {
545        if (ch < CHAR_STRING_ARRAY.length) {
546            return CHAR_STRING_ARRAY[ch];
547        }
548        return String.valueOf(ch);
549    }
550
551    /**
552     * Converts the character to a String that contains the one character.
553     *
554     * <p>
555     * For ASCII 7 bit characters, this uses a cache that will return the
556     * same String object each time.
557     * </p>
558     *
559     * <p>
560     * If {@code null} is passed in, {@code null} will be returned.
561     * </p>
562     *
563     * <pre>
564     *   CharUtils.toString(null) = null
565     *   CharUtils.toString(' ')  = " "
566     *   CharUtils.toString('A')  = "A"
567     * </pre>
568     *
569     * @param ch  The character to convert
570     * @return A String containing the one specified character
571     */
572    public static String toString(final Character ch) {
573        return ch != null ? toString(ch.charValue()) : null;
574    }
575
576    /**
577     * Converts the string to the Unicode format '\u0020'.
578     *
579     * <p>
580     * This format is the Java source code format.
581     * </p>
582     *
583     * <pre>
584     *   CharUtils.unicodeEscaped(' ') = "\u0020"
585     *   CharUtils.unicodeEscaped('A') = "\u0041"
586     * </pre>
587     *
588     * @param ch  The character to convert
589     * @return The escaped Unicode string
590     */
591    public static String unicodeEscaped(final char ch) {
592        return "\\u" +
593            HEX_DIGITS[ch >> 12 & 15] +
594            HEX_DIGITS[ch >> 8 & 15] +
595            HEX_DIGITS[ch >> 4 & 15] +
596            HEX_DIGITS[ch & 15];
597    }
598
599    /**
600     * Converts the string to the Unicode format '\u0020'.
601     *
602     * <p>
603     * This format is the Java source code format.
604     * </p>
605     *
606     * <p>
607     * If {@code null} is passed in, {@code null} will be returned.
608     * </p>
609     *
610     * <pre>
611     *   CharUtils.unicodeEscaped(null) = null
612     *   CharUtils.unicodeEscaped(' ')  = "\u0020"
613     *   CharUtils.unicodeEscaped('A')  = "\u0041"
614     * </pre>
615     *
616     * @param ch  The character to convert, may be null
617     * @return The escaped Unicode string, null if null input
618     */
619    public static String unicodeEscaped(final Character ch) {
620        return ch != null ? unicodeEscaped(ch.charValue()) : null;
621    }
622
623    /**
624     * {@link CharUtils} instances should NOT be constructed in standard programming.
625     * Instead, the class should be used as {@code CharUtils.toString('c');}.
626     *
627     * <p>
628     * This constructor is public to permit tools that require a JavaBean instance
629     * to operate.
630     * </p>
631     *
632     * @deprecated TODO Make private in 4.0.
633     */
634    @Deprecated
635    public CharUtils() {
636        // empty
637    }
638}