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