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