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.lang3; 18 19 import org.apache.commons.lang3.stream.Streams; 20 21 /** 22 * Operations on {@link CharSet} instances. 23 * 24 * <p>This class handles {@code null} input gracefully. 25 * An exception will not be thrown for a {@code null} input. 26 * Each method documents its behavior in more detail.</p> 27 * 28 * <p>#ThreadSafe#</p> 29 * @see CharSet 30 * @since 1.0 31 */ 32 public class CharSetUtils { 33 34 /** 35 * Takes an argument in set-syntax, see evaluateSet, 36 * and identifies whether any of the characters are present in the specified string. 37 * 38 * <pre> 39 * CharSetUtils.containsAny(null, *) = false 40 * CharSetUtils.containsAny("", *) = false 41 * CharSetUtils.containsAny(*, null) = false 42 * CharSetUtils.containsAny(*, "") = false 43 * CharSetUtils.containsAny("hello", "k-p") = true 44 * CharSetUtils.containsAny("hello", "a-d") = false 45 * </pre> 46 * 47 * @see CharSet#getInstance(String...) for set-syntax. 48 * @param str String to look for characters in, may be null 49 * @param set String[] set of characters to identify, may be null 50 * @return whether or not the characters in the set are in the primary string 51 * @since 3.2 52 */ 53 public static boolean containsAny(final String str, final String... set) { 54 if (StringUtils.isEmpty(str) || deepEmpty(set)) { 55 return false; 56 } 57 final CharSet chars = CharSet.getInstance(set); 58 for (final char c : str.toCharArray()) { 59 if (chars.contains(c)) { 60 return true; 61 } 62 } 63 return false; 64 } 65 66 /** 67 * Takes an argument in set-syntax, see evaluateSet, 68 * and returns the number of characters present in the specified string. 69 * 70 * <pre> 71 * CharSetUtils.count(null, *) = 0 72 * CharSetUtils.count("", *) = 0 73 * CharSetUtils.count(*, null) = 0 74 * CharSetUtils.count(*, "") = 0 75 * CharSetUtils.count("hello", "k-p") = 3 76 * CharSetUtils.count("hello", "a-e") = 1 77 * </pre> 78 * 79 * @see CharSet#getInstance(String...) for set-syntax. 80 * @param str String to count characters in, may be null 81 * @param set String[] set of characters to count, may be null 82 * @return the character count, zero if null string input 83 */ 84 public static int count(final String str, final String... set) { 85 if (StringUtils.isEmpty(str) || deepEmpty(set)) { 86 return 0; 87 } 88 final CharSet chars = CharSet.getInstance(set); 89 int count = 0; 90 for (final char c : str.toCharArray()) { 91 if (chars.contains(c)) { 92 count++; 93 } 94 } 95 return count; 96 } 97 98 /** 99 * Determines whether or not all the Strings in an array are 100 * empty or not. 101 * 102 * @param strings String[] whose elements are being checked for emptiness 103 * @return whether or not the String is empty 104 */ 105 private static boolean deepEmpty(final String[] strings) { 106 return Streams.of(strings).allMatch(StringUtils::isEmpty); 107 } 108 109 /** 110 * Takes an argument in set-syntax, see evaluateSet, 111 * and deletes any of characters present in the specified string. 112 * 113 * <pre> 114 * CharSetUtils.delete(null, *) = null 115 * CharSetUtils.delete("", *) = "" 116 * CharSetUtils.delete(*, null) = * 117 * CharSetUtils.delete(*, "") = * 118 * CharSetUtils.delete("hello", "hl") = "eo" 119 * CharSetUtils.delete("hello", "le") = "ho" 120 * </pre> 121 * 122 * @see CharSet#getInstance(String...) for set-syntax. 123 * @param str String to delete characters from, may be null 124 * @param set String[] set of characters to delete, may be null 125 * @return the modified String, {@code null} if null string input 126 */ 127 public static String delete(final String str, final String... set) { 128 if (StringUtils.isEmpty(str) || deepEmpty(set)) { 129 return str; 130 } 131 return modify(str, set, false); 132 } 133 134 /** 135 * Takes an argument in set-syntax, see evaluateSet, 136 * and keeps any of characters present in the specified string. 137 * 138 * <pre> 139 * CharSetUtils.keep(null, *) = null 140 * CharSetUtils.keep("", *) = "" 141 * CharSetUtils.keep(*, null) = "" 142 * CharSetUtils.keep(*, "") = "" 143 * CharSetUtils.keep("hello", "hl") = "hll" 144 * CharSetUtils.keep("hello", "le") = "ell" 145 * </pre> 146 * 147 * @see CharSet#getInstance(String...) for set-syntax. 148 * @param str String to keep characters from, may be null 149 * @param set String[] set of characters to keep, may be null 150 * @return the modified String, {@code null} if null string input 151 * @since 2.0 152 */ 153 public static String keep(final String str, final String... set) { 154 if (str == null) { 155 return null; 156 } 157 if (str.isEmpty() || deepEmpty(set)) { 158 return StringUtils.EMPTY; 159 } 160 return modify(str, set, true); 161 } 162 163 /** 164 * Implements delete and keep. 165 * 166 * @param str String to modify characters within 167 * @param set String[] set of characters to modify 168 * @param expect whether to evaluate on match, or non-match 169 * @return the modified String, not null 170 */ 171 private static String modify(final String str, final String[] set, final boolean expect) { 172 final CharSet chars = CharSet.getInstance(set); 173 final StringBuilder buffer = new StringBuilder(str.length()); 174 final char[] chrs = str.toCharArray(); 175 for (final char chr : chrs) { 176 if (chars.contains(chr) == expect) { 177 buffer.append(chr); 178 } 179 } 180 return buffer.toString(); 181 } 182 183 /** 184 * Squeezes any repetitions of a character that is mentioned in the 185 * supplied set. 186 * 187 * <pre> 188 * CharSetUtils.squeeze(null, *) = null 189 * CharSetUtils.squeeze("", *) = "" 190 * CharSetUtils.squeeze(*, null) = * 191 * CharSetUtils.squeeze(*, "") = * 192 * CharSetUtils.squeeze("hello", "k-p") = "helo" 193 * CharSetUtils.squeeze("hello", "a-e") = "hello" 194 * </pre> 195 * 196 * @see CharSet#getInstance(String...) for set-syntax. 197 * @param str the string to squeeze, may be null 198 * @param set the character set to use for manipulation, may be null 199 * @return the modified String, {@code null} if null string input 200 */ 201 public static String squeeze(final String str, final String... set) { 202 if (StringUtils.isEmpty(str) || deepEmpty(set)) { 203 return str; 204 } 205 final CharSet chars = CharSet.getInstance(set); 206 final StringBuilder buffer = new StringBuilder(str.length()); 207 final char[] chrs = str.toCharArray(); 208 final int sz = chrs.length; 209 char lastChar = chrs[0]; 210 char ch; 211 Character inChars = null; 212 Character notInChars = null; 213 buffer.append(lastChar); 214 for (int i = 1; i < sz; i++) { 215 ch = chrs[i]; 216 if (ch == lastChar) { 217 if (inChars != null && ch == inChars) { 218 continue; 219 } 220 if (notInChars == null || ch != notInChars) { 221 if (chars.contains(ch)) { 222 inChars = ch; 223 continue; 224 } 225 notInChars = ch; 226 } 227 } 228 buffer.append(ch); 229 lastChar = ch; 230 } 231 return buffer.toString(); 232 } 233 234 /** 235 * CharSetUtils instances should NOT be constructed in standard programming. 236 * Instead, the class should be used as {@code CharSetUtils.evaluateSet(null);}. 237 * 238 * <p>This constructor is public to permit tools that require a JavaBean instance 239 * to operate.</p> 240 * 241 * @deprecated TODO Make private in 4.0. 242 */ 243 @Deprecated 244 public CharSetUtils() { 245 } 246 }