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