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 org.apache.commons.lang3.stream.Streams; 020 021/** 022 * Operations on {@link CharSet} instances. 023 * 024 * <p> 025 * This class handles {@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 * @see CharSet 035 * @since 1.0 036 */ 037public class CharSetUtils { 038 039 /** 040 * Takes an argument in set-syntax, see evaluateSet, 041 * and identifies whether any of the characters are present in the specified string. 042 * 043 * <pre> 044 * CharSetUtils.containsAny(null, *) = false 045 * CharSetUtils.containsAny("", *) = false 046 * CharSetUtils.containsAny(*, null) = false 047 * CharSetUtils.containsAny(*, "") = false 048 * CharSetUtils.containsAny("hello", "k-p") = true 049 * CharSetUtils.containsAny("hello", "a-d") = false 050 * </pre> 051 * 052 * @see CharSet#getInstance(String...) for set-syntax. 053 * @param str String to look for characters in, may be null 054 * @param set String[] set of characters to identify, may be null 055 * @return whether or not the characters in the set are in the primary string 056 * @since 3.2 057 */ 058 public static boolean containsAny(final String str, final String... set) { 059 if (isEmpty(str, set)) { 060 return false; 061 } 062 final CharSet chars = CharSet.getInstance(set); 063 for (final char c : str.toCharArray()) { 064 if (chars.contains(c)) { 065 return true; 066 } 067 } 068 return false; 069 } 070 071 /** 072 * Takes an argument in set-syntax, see evaluateSet, 073 * and returns the number of characters present in the specified string. 074 * 075 * <pre> 076 * CharSetUtils.count(null, *) = 0 077 * CharSetUtils.count("", *) = 0 078 * CharSetUtils.count(*, null) = 0 079 * CharSetUtils.count(*, "") = 0 080 * CharSetUtils.count("hello", "k-p") = 3 081 * CharSetUtils.count("hello", "a-e") = 1 082 * </pre> 083 * 084 * @see CharSet#getInstance(String...) for set-syntax. 085 * @param str String to count characters in, may be null 086 * @param set String[] set of characters to count, may be null 087 * @return The character count, zero if null string input 088 */ 089 public static int count(final String str, final String... set) { 090 if (isEmpty(str, set)) { 091 return 0; 092 } 093 final CharSet chars = CharSet.getInstance(set); 094 int count = 0; 095 for (final char c : str.toCharArray()) { 096 if (chars.contains(c)) { 097 count++; 098 } 099 } 100 return count; 101 } 102 103 /** 104 * Determines whether or not all the Strings in an array are 105 * empty or not. 106 * 107 * @param strings String[] whose elements are being checked for emptiness 108 * @return whether or not the String is empty 109 */ 110 private static boolean deepEmpty(final String[] strings) { 111 return Streams.of(strings).allMatch(StringUtils::isEmpty); 112 } 113 114 /** 115 * Takes an argument in set-syntax, see evaluateSet, 116 * and deletes any of characters present in the specified string. 117 * 118 * <pre> 119 * CharSetUtils.delete(null, *) = null 120 * CharSetUtils.delete("", *) = "" 121 * CharSetUtils.delete(*, null) = * 122 * CharSetUtils.delete(*, "") = * 123 * CharSetUtils.delete("hello", "hl") = "eo" 124 * CharSetUtils.delete("hello", "le") = "ho" 125 * </pre> 126 * 127 * @see CharSet#getInstance(String...) for set-syntax. 128 * @param str String to delete characters from, may be null 129 * @param set String[] set of characters to delete, may be null 130 * @return The modified String, {@code null} if null string input 131 */ 132 public static String delete(final String str, final String... set) { 133 if (isEmpty(str, set)) { 134 return str; 135 } 136 return modify(str, set, false); 137 } 138 139 private static boolean isEmpty(final String str, final String... set) { 140 return StringUtils.isEmpty(str) || deepEmpty(set); 141 } 142 143 /** 144 * Takes an argument in set-syntax, see evaluateSet, 145 * and keeps any of characters present in the specified string. 146 * 147 * <pre> 148 * CharSetUtils.keep(null, *) = null 149 * CharSetUtils.keep("", *) = "" 150 * CharSetUtils.keep(*, null) = "" 151 * CharSetUtils.keep(*, "") = "" 152 * CharSetUtils.keep("hello", "hl") = "hll" 153 * CharSetUtils.keep("hello", "le") = "ell" 154 * </pre> 155 * 156 * @see CharSet#getInstance(String...) for set-syntax. 157 * @param str String to keep characters from, may be null 158 * @param set String[] set of characters to keep, may be null 159 * @return The modified String, {@code null} if null string input 160 * @since 2.0 161 */ 162 public static String keep(final String str, final String... set) { 163 if (str == null) { 164 return null; 165 } 166 if (str.isEmpty() || deepEmpty(set)) { 167 return StringUtils.EMPTY; 168 } 169 return modify(str, set, true); 170 } 171 172 /** 173 * Implements delete and keep. 174 * 175 * @param str String to modify characters within 176 * @param set String[] set of characters to modify 177 * @param expect whether to evaluate on match, or non-match 178 * @return The modified String, not null 179 */ 180 private static String modify(final String str, final String[] set, final boolean expect) { 181 final CharSet chars = CharSet.getInstance(set); 182 final StringBuilder buffer = new StringBuilder(str.length()); 183 final char[] chrs = str.toCharArray(); 184 for (final char chr : chrs) { 185 if (chars.contains(chr) == expect) { 186 buffer.append(chr); 187 } 188 } 189 return buffer.toString(); 190 } 191 192 /** 193 * Squeezes any repetitions of a character that is mentioned in the 194 * supplied set. 195 * 196 * <pre> 197 * CharSetUtils.squeeze(null, *) = null 198 * CharSetUtils.squeeze("", *) = "" 199 * CharSetUtils.squeeze(*, null) = * 200 * CharSetUtils.squeeze(*, "") = * 201 * CharSetUtils.squeeze("hello", "k-p") = "helo" 202 * CharSetUtils.squeeze("hello", "a-e") = "hello" 203 * </pre> 204 * 205 * @see CharSet#getInstance(String...) for set-syntax. 206 * @param str The string to squeeze, may be null 207 * @param set The character set to use for manipulation, may be null 208 * @return The modified String, {@code null} if null string input 209 */ 210 public static String squeeze(final String str, final String... set) { 211 if (isEmpty(str, set)) { 212 return str; 213 } 214 final CharSet chars = CharSet.getInstance(set); 215 final StringBuilder buffer = new StringBuilder(str.length()); 216 final char[] chrs = str.toCharArray(); 217 final int sz = chrs.length; 218 char lastChar = chrs[0]; 219 char ch; 220 Character inChars = null; 221 Character notInChars = null; 222 buffer.append(lastChar); 223 for (int i = 1; i < sz; i++) { 224 ch = chrs[i]; 225 if (ch == lastChar) { 226 if (inChars != null && ch == inChars) { 227 continue; 228 } 229 if (notInChars == null || ch != notInChars) { 230 if (chars.contains(ch)) { 231 inChars = ch; 232 continue; 233 } 234 notInChars = ch; 235 } 236 } 237 buffer.append(ch); 238 lastChar = ch; 239 } 240 return buffer.toString(); 241 } 242 243 /** 244 * CharSetUtils instances should NOT be constructed in standard programming. 245 * Instead, the class should be used as {@code CharSetUtils.evaluateSet(null);}. 246 * 247 * <p> 248 * This constructor is public to permit tools that require a JavaBean instance 249 * to operate. 250 * </p> 251 * 252 * @deprecated TODO Make private in 4.0. 253 */ 254 @Deprecated 255 public CharSetUtils() { 256 } 257}