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