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