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.juneau.commons.lang; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import org.apache.juneau.commons.utils.*; 022 023/** 024 * A simple mutable character value. 025 * 026 * <p> 027 * This class extends {@link Holder}<{@link Character}> and provides a convenient way to pass mutable 028 * character references to lambdas, inner classes, or methods. 029 * 030 * <h5 class='section'>Notes:</h5><ul> 031 * <li class='note'> 032 * This class is <b>not thread-safe</b>. 033 * </ul> 034 * 035 * <h5 class='section'>Example:</h5> 036 * <p class='bjava'> 037 * <jc>// Create a character value to track the last character seen</jc> 038 * CharHolder <jv>lastChar</jv> = CharHolder.<jsm>create</jsm>(); 039 * 040 * <jc>// Use in a lambda to track state</jc> 041 * charStream.forEach(<jv>ch</jv> -> { 042 * <jk>if</jk> (Character.isUpperCase(<jv>ch</jv>)) { 043 * <jv>lastChar</jv>.set(<jv>ch</jv>); 044 * } 045 * }); 046 * 047 * <jsm>log</jsm>(<js>"Last uppercase char: "</js> + <jv>lastChar</jv>.get()); 048 * </p> 049 * 050 * <h5 class='section'>See Also:</h5><ul> 051 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsLang">Lang Package</a> 052 * </ul> 053 */ 054@SuppressWarnings({ 055 "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants 056}) 057public class CharHolder extends Holder<Character> { 058 059 /** 060 * Creates a new character value initialized to <c>'\0'</c> (null character). 061 * 062 * <h5 class='section'>Example:</h5> 063 * <p class='bjava'> 064 * CharHolder <jv>value</jv> = CharHolder.<jsm>create</jsm>(); 065 * <jsm>assertEquals</jsm>('\0', <jv>value</jv>.get()); 066 * </p> 067 * 068 * @return A new character value. 069 */ 070 public static CharHolder create() { 071 return of('\0'); 072 } 073 074 /** 075 * Creates a new character value with the specified initial value. 076 * 077 * <h5 class='section'>Example:</h5> 078 * <p class='bjava'> 079 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('A'); 080 * <jsm>assertEquals</jsm>('A', <jv>value</jv>.get()); 081 * </p> 082 * 083 * @param value The initial value. Can be <jk>null</jk>. 084 * @return A new character value. 085 */ 086 public static CharHolder of(Character value) { 087 return new CharHolder(value); 088 } 089 090 /** 091 * Constructor. 092 * 093 * @param value The initial value. Can be <jk>null</jk>. 094 */ 095 public CharHolder(Character value) { 096 super(value); 097 } 098 099 /** 100 * Adds the specified value to the current value. 101 * 102 * <h5 class='section'>Example:</h5> 103 * <p class='bjava'> 104 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('A'); 105 * <jv>value</jv>.add((<jk>char</jk>)5); 106 * <jsm>assertEquals</jsm>('F', <jv>value</jv>.get()); 107 * </p> 108 * 109 * @param x The value to add. Can be <jk>null</jk> (treated as <c>0</c>). 110 * @return This object. 111 */ 112 public CharHolder add(Character x) { 113 var v = get(); 114 set((char)((v == null ? 0 : v) + (x == null ? 0 : x))); 115 return this; 116 } 117 118 /** 119 * Adds the specified value to the current value and returns the new value. 120 * 121 * <h5 class='section'>Example:</h5> 122 * <p class='bjava'> 123 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('A'); 124 * <jk>char</jk> <jv>result</jv> = <jv>value</jv>.addAndGet((<jk>char</jk>)5); <jc>// Returns 'F'</jc> 125 * <jsm>assertEquals</jsm>('F', <jv>value</jv>.get()); 126 * </p> 127 * 128 * @param x The value to add. Can be <jk>null</jk> (treated as <c>0</c>). 129 * @return The new value after addition. 130 */ 131 public Character addAndGet(Character x) { 132 var v = get(); 133 var result = (char)((v == null ? 0 : v) + (x == null ? 0 : x)); 134 set(result); 135 return result; 136 } 137 138 /** 139 * Decrements the value by 1. 140 * 141 * <h5 class='section'>Example:</h5> 142 * <p class='bjava'> 143 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('B'); 144 * <jv>value</jv>.decrement(); 145 * <jsm>assertEquals</jsm>('A', <jv>value</jv>.get()); 146 * </p> 147 * 148 * @return This object. 149 */ 150 public CharHolder decrement() { 151 var v = get(); 152 set((char)((v == null ? 0 : v) - 1)); 153 return this; 154 } 155 156 /** 157 * Decrements the value by 1 and returns the new value. 158 * 159 * <h5 class='section'>Example:</h5> 160 * <p class='bjava'> 161 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('B'); 162 * <jk>char</jk> <jv>result</jv> = <jv>value</jv>.decrementAndGet(); <jc>// Returns 'A'</jc> 163 * <jsm>assertEquals</jsm>('A', <jv>value</jv>.get()); 164 * </p> 165 * 166 * @return The decremented value. 167 */ 168 public Character decrementAndGet() { 169 var v = get(); 170 var result = (char)((v == null ? 0 : v) - 1); 171 set(result); 172 return result; 173 } 174 175 /** 176 * Increments the value by 1. 177 * 178 * <h5 class='section'>Example:</h5> 179 * <p class='bjava'> 180 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('A'); 181 * <jv>value</jv>.increment(); 182 * <jsm>assertEquals</jsm>('B', <jv>value</jv>.get()); 183 * </p> 184 * 185 * @return This object. 186 */ 187 public CharHolder increment() { 188 var v = get(); 189 set((char)((v == null ? 0 : v) + 1)); 190 return this; 191 } 192 193 /** 194 * Increments the value by 1 and returns the new value. 195 * 196 * <h5 class='section'>Example:</h5> 197 * <p class='bjava'> 198 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('A'); 199 * <jk>char</jk> <jv>result</jv> = <jv>value</jv>.incrementAndGet(); <jc>// Returns 'B'</jc> 200 * <jsm>assertEquals</jsm>('B', <jv>value</jv>.get()); 201 * </p> 202 * 203 * @return The incremented value. 204 */ 205 public Character incrementAndGet() { 206 var v = get(); 207 var result = (char)((v == null ? 0 : v) + 1); 208 set(result); 209 return result; 210 } 211 212 /** 213 * Checks if the current value is equal to the specified character. 214 * 215 * <p> 216 * Uses {@link Shorts#eq(Object, Object)} for deep equality comparison, which handles nulls safely. 217 * 218 * <h5 class='section'>Example:</h5> 219 * <p class='bjava'> 220 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('A'); 221 * <jsm>assertTrue</jsm>(<jv>value</jv>.is('A')); 222 * <jsm>assertFalse</jsm>(<jv>value</jv>.is('B')); 223 * </p> 224 * 225 * @param value The character to compare to. Can be <jk>null</jk>. 226 * @return <jk>true</jk> if the current value is equal to the specified character. 227 */ 228 @Override 229 public boolean is(Character value) { 230 return eq(get(), value); 231 } 232 233 /** 234 * Checks if the current value matches any of the specified characters. 235 * 236 * <p> 237 * Uses {@link Shorts#eq(Object, Object)} for deep equality comparison of each character. 238 * 239 * <h5 class='section'>Example:</h5> 240 * <p class='bjava'> 241 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('B'); 242 * <jsm>assertTrue</jsm>(<jv>value</jv>.isAny('A', 'B', 'C')); 243 * <jsm>assertFalse</jsm>(<jv>value</jv>.isAny('X', 'Y')); 244 * </p> 245 * 246 * @param values The characters to compare to. Must not be <jk>null</jk>. 247 * @return <jk>true</jk> if the current value matches any of the specified characters. 248 */ 249 public boolean isAny(Character...values) { 250 reqnn("values", values); 251 var current = get(); 252 for (var value : values) 253 if (eq(current, value)) 254 return true; 255 return false; 256 } 257 258 /** 259 * Checks if the current value matches any character in the specified string. 260 * 261 * <h5 class='section'>Example:</h5> 262 * <p class='bjava'> 263 * CharHolder <jv>value</jv> = CharHolder.<jsm>of</jsm>('B'); 264 * <jsm>assertTrue</jsm>(<jv>value</jv>.isAny(<js>"ABC"</js>)); 265 * <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(<js>"XYZ"</js>)); 266 * 267 * <jc>// Null/empty string returns false</jc> 268 * <jsm>assertFalse</jsm>(<jv>value</jv>.isAny((<jk>null</jk>)); 269 * <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(<js>""</js>)); 270 * </p> 271 * 272 * @param values The string containing characters to compare to. Can be <jk>null</jk> or empty. 273 * @return <jk>true</jk> if the current value matches any character in the string. 274 */ 275 public boolean isAny(String values) { 276 if (ie(values)) 277 return false; 278 var current = get(); 279 if (current == null) 280 return false; 281 return values.indexOf(current) >= 0; 282 } 283}