CaseUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.text;

  18. import java.util.HashSet;
  19. import java.util.Set;

  20. import org.apache.commons.lang3.ArrayUtils;
  21. import org.apache.commons.lang3.StringUtils;

  22. /**
  23.  * Case manipulation operations on Strings that contain words.
  24.  *
  25.  * <p>This class tries to handle {@code null} input gracefully.
  26.  * An exception will not be thrown for a {@code null} input.
  27.  * Each method documents its behavior in more detail.</p>
  28.  *
  29.  * @since 1.2
  30.  */
  31. public class CaseUtils {

  32.     /**
  33.      * Converts all the delimiter separated words in a String into camelCase,
  34.      * that is each word is made up of a title case character and then a series of
  35.      * lowercase characters.
  36.      *
  37.      * <p>The delimiters represent a set of characters understood to separate words.
  38.      * The first non-delimiter character after a delimiter will be capitalized. The first String
  39.      * character may or may not be capitalized and it's determined by the user input for capitalizeFirstLetter
  40.      * variable.</p>
  41.      *
  42.      * <p>A {@code null} input String returns {@code null}.</p>
  43.      *
  44.      * <p>A input string with only delimiter characters returns {@code ""}.</p>
  45.      *
  46.      * Capitalization uses the Unicode title case, normally equivalent to
  47.      * upper case and cannot perform locale-sensitive mappings.
  48.      *
  49.      * <pre>
  50.      * CaseUtils.toCamelCase(null, false)                                 = null
  51.      * CaseUtils.toCamelCase("", false, *)                                = ""
  52.      * CaseUtils.toCamelCase(*, false, null)                              = *
  53.      * CaseUtils.toCamelCase(*, true, new char[0])                        = *
  54.      * CaseUtils.toCamelCase("To.Camel.Case", false, new char[]{'.'})     = "toCamelCase"
  55.      * CaseUtils.toCamelCase(" to @ Camel case", true, new char[]{'@'})   = "ToCamelCase"
  56.      * CaseUtils.toCamelCase(" @to @ Camel case", false, new char[]{'@'}) = "toCamelCase"
  57.      * CaseUtils.toCamelCase(" @", false, new char[]{'@'})                = ""
  58.      * </pre>
  59.      *
  60.      * @param str  the String to be converted to camelCase, may be null
  61.      * @param capitalizeFirstLetter boolean that determines if the first character of first word should be title case.
  62.      * @param delimiters  set of characters to determine capitalization, null and/or empty array means whitespace
  63.      * @return camelCase of String, {@code null} if null String input
  64.      */
  65.     public static String toCamelCase(String str, final boolean capitalizeFirstLetter, final char... delimiters) {
  66.         if (StringUtils.isEmpty(str)) {
  67.             return str;
  68.         }
  69.         str = str.toLowerCase();
  70.         final int strLen = str.length();
  71.         final int[] newCodePoints = new int[strLen];
  72.         int outOffset = 0;
  73.         final Set<Integer> delimiterSet = toDelimiterSet(delimiters);
  74.         boolean capitalizeNext = capitalizeFirstLetter;
  75.         for (int index = 0; index < strLen;) {
  76.             final int codePoint = str.codePointAt(index);

  77.             if (delimiterSet.contains(codePoint)) {
  78.                 capitalizeNext = outOffset != 0;
  79.                 index += Character.charCount(codePoint);
  80.             } else if (capitalizeNext || outOffset == 0 && capitalizeFirstLetter) {
  81.                 final int titleCaseCodePoint = Character.toTitleCase(codePoint);
  82.                 newCodePoints[outOffset++] = titleCaseCodePoint;
  83.                 index += Character.charCount(titleCaseCodePoint);
  84.                 capitalizeNext = false;
  85.             } else {
  86.                 newCodePoints[outOffset++] = codePoint;
  87.                 index += Character.charCount(codePoint);
  88.             }
  89.         }

  90.         return new String(newCodePoints, 0, outOffset);
  91.     }

  92.     /**
  93.      * Converts an array of delimiters to a hash set of code points. Code point of space(32) is added
  94.      * as the default value. The generated hash set provides O(1) lookup time.
  95.      *
  96.      * @param delimiters  set of characters to determine capitalization, null means whitespace
  97.      * @return Set<Integer>
  98.      */
  99.     private static Set<Integer> toDelimiterSet(final char[] delimiters) {
  100.         final Set<Integer> delimiterHashSet = new HashSet<>();
  101.         delimiterHashSet.add(Character.codePointAt(new char[]{' '}, 0));
  102.         if (ArrayUtils.isEmpty(delimiters)) {
  103.             return delimiterHashSet;
  104.         }

  105.         for (int index = 0; index < delimiters.length; index++) {
  106.             delimiterHashSet.add(Character.codePointAt(delimiters, index));
  107.         }
  108.         return delimiterHashSet;
  109.     }

  110.     /**
  111.      * {@code CaseUtils} instances should NOT be constructed in
  112.      * standard programming. Instead, the class should be used as
  113.      * {@code CaseUtils.toCamelCase("foo bar", true, new char[]{'-'});}.
  114.      *
  115.      * <p>This constructor is public to permit tools that require a JavaBean
  116.      * instance to operate.</p>
  117.      */
  118.     public CaseUtils() {
  119.     }
  120. }