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}&lt;{@link Character}&gt; 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> -&gt; {
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}