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 string value.
025 *
026 * <p>
027 * This class extends {@link Holder}&lt;{@link String}&gt; and adds convenience methods for string
028 * equality testing, which are useful in lambdas and conditional logic.
029 *
030 * <h5 class='section'>Notes:</h5><ul>
031 *    <li class='note'>
032 *       This class is <b>not thread-safe</b>. For concurrent access, use synchronization or atomic classes.
033 * </ul>
034 *
035 * <h5 class='section'>Example:</h5>
036 * <p class='bjava'>
037 *    <jc>// Create a mutable string</jc>
038 *    StringHolder <jv>name</jv> = StringHolder.<jsm>of</jsm>(<js>"John"</js>);
039 *
040 *    <jc>// Check equality</jc>
041 *    <jk>if</jk> (<jv>name</jv>.is(<js>"John"</js>)) {
042 *       <jsm>System</jsm>.<jsf>out</jsf>.println(<js>"Name is John"</js>);
043 *    }
044 *
045 *    <jc>// Check multiple values</jc>
046 *    <jk>if</jk> (<jv>name</jv>.isAny(<js>"John"</js>, <js>"Jane"</js>, <js>"Bob"</js>)) {
047 *       <jsm>System</jsm>.<jsf>out</jsf>.println(<js>"Name found"</js>);
048 *    }
049 * </p>
050 *
051 * <h5 class='section'>See Also:</h5><ul>
052 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsLang">Lang Package</a>
053 * </ul>
054 */
055@SuppressWarnings({
056   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
057})
058public class StringHolder extends Holder<String> {
059
060   /**
061    * Creates a new empty string value.
062    *
063    * <h5 class='section'>Example:</h5>
064    * <p class='bjava'>
065    *    StringHolder <jv>name</jv> = StringHolder.<jsm>create</jsm>();
066    *    <jsm>assertNull</jsm>(<jv>name</jv>.get());
067    * </p>
068    *
069    * @return A new string value.
070    */
071   public static StringHolder create() {
072      return of(null);
073   }
074
075   /**
076    * Creates a new string value with the specified initial value.
077    *
078    * <h5 class='section'>Example:</h5>
079    * <p class='bjava'>
080    *    StringHolder <jv>name</jv> = StringHolder.<jsm>of</jsm>(<js>"Hello"</js>);
081    *    <jsm>assertEquals</jsm>(<js>"Hello"</js>, <jv>name</jv>.get());
082    * </p>
083    *
084    * @param value The initial value.  Can be <jk>null</jk>.
085    * @return A new string value.
086    */
087   public static StringHolder of(String value) {
088      return new StringHolder(value);
089   }
090
091   /**
092    * Constructor.
093    */
094   public StringHolder() {
095      super(null);
096   }
097
098   /**
099    * Constructor.
100    *
101    * @param value The initial value.  Can be <jk>null</jk>.
102    */
103   public StringHolder(String value) {
104      super(value);
105   }
106
107   /**
108    * Checks if the current value is equal to the specified value.
109    *
110    * <p>
111    * Uses {@link Shorts#eq(Object, Object)} for deep equality comparison, which handles nulls safely.
112    *
113    * <h5 class='section'>Example:</h5>
114    * <p class='bjava'>
115    *    StringHolder <jv>name</jv> = StringHolder.<jsm>of</jsm>(<js>"John"</js>);
116    *    <jsm>assertTrue</jsm>(<jv>name</jv>.is(<js>"John"</js>));
117    *    <jsm>assertFalse</jsm>(<jv>name</jv>.is(<js>"Jane"</js>));
118    *
119    *    <jc>// Handles null safely</jc>
120    *    StringHolder <jv>empty</jv> = StringHolder.<jsm>create</jsm>();
121    *    <jsm>assertTrue</jsm>(<jv>empty</jv>.is(<jk>null</jk>));
122    * </p>
123    *
124    * @param value The value to compare to.  Can be <jk>null</jk>.
125    * @return <jk>true</jk> if the current value is equal to the specified value.
126    */
127   @Override
128   public boolean is(String value) {
129      return eq(get(), value);
130   }
131
132   /**
133    * Checks if the current value matches any of the specified values.
134    *
135    * <p>
136    * Uses {@link Shorts#eq(Object, Object)} for deep equality comparison of each value.
137    *
138    * <h5 class='section'>Example:</h5>
139    * <p class='bjava'>
140    *    StringHolder <jv>name</jv> = StringHolder.<jsm>of</jsm>(<js>"John"</js>);
141    *    <jsm>assertTrue</jsm>(<jv>name</jv>.isAny(<js>"John"</js>, <js>"Jane"</js>, <js>"Bob"</js>));
142    *    <jsm>assertFalse</jsm>(<jv>name</jv>.isAny(<js>"Alice"</js>, <js>"Charlie"</js>));
143    *
144    *    <jc>// Empty array returns false</jc>
145    *    <jsm>assertFalse</jsm>(<jv>name</jv>.isAny());
146    * </p>
147    *
148    * @param values The values to compare to.  Must not be <jk>null</jk>.
149    * @return <jk>true</jk> if the current value matches any of the specified values.
150    */
151   public boolean isAny(String...values) {
152      reqnn("values", values);
153      var current = get();
154      for (var value : values)
155         if (eq(current, value))
156            return true;
157      return false;
158   }
159}