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.collections;
018
019
020import java.util.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022
023/**
024 * A fluent wrapper around an arbitrary list that provides convenient methods for adding elements.
025 *
026 * <p>
027 * This class wraps an underlying list and provides a fluent API for adding elements. All methods return
028 * <c>this</c> to allow method chaining. The underlying list can be any {@link List} implementation.
029 *
030 * <h5 class='section'>Features:</h5>
031 * <ul class='spaced-list'>
032 *    <li><b>Fluent API:</b> All methods return <c>this</c> for method chaining
033 *    <li><b>Arbitrary List Support:</b> Works with any list implementation
034 *    <li><b>Conditional Adding:</b> Add elements conditionally based on boolean expressions
035 *    <li><b>Transparent Interface:</b> Implements the full {@link List} interface, so it can be used anywhere a list is expected
036 * </ul>
037 *
038 * <h5 class='section'>Usage:</h5>
039 * <p class='bjava'>
040 *    <jc>// Create a FluentList wrapping an ArrayList</jc>
041 *    FluentList&lt;String&gt; <jv>list</jv> = <jk>new</jk> FluentList&lt;&gt;(<jk>new</jk> ArrayList&lt;&gt;());
042 *
043 *    <jv>list</jv>
044 *       .a(<js>"item1"</js>)
045 *       .a(<js>"item2"</js>)
046 *       .ai(<jk>true</jk>, <js>"item3"</js>)   <jc>// Added</jc>
047 *       .ai(<jk>false</jk>, <js>"item4"</js>); <jc>// Not added</jc>
048 *
049 *    <jc>// Add all elements from another collection</jc>
050 *    List&lt;String&gt; <jv>other</jv> = List.of(<js>"item5"</js>, <js>"item6"</js>);
051 *    <jv>list</jv>.aa(<jv>other</jv>);
052 * </p>
053 *
054 * <h5 class='section'>Example - Conditional Building:</h5>
055 * <p class='bjava'>
056 *    <jk>boolean</jk> <jv>includeDebug</jv> = <jk>true</jk>;
057 *    <jk>boolean</jk> <jv>includeTest</jv> = <jk>false</jk>;
058 *
059 *    FluentList&lt;String&gt; <jv>config</jv> = <jk>new</jk> FluentList&lt;&gt;(<jk>new</jk> ArrayList&lt;&gt;())
060 *       .a(<js>"setting1"</js>)
061 *       .a(<js>"setting2"</js>)
062 *       .ai(<jv>includeDebug</jv>, <js>"debug"</js>)   <jc>// Added</jc>
063 *       .ai(<jv>includeTest</jv>, <js>"test"</js>);    <jc>// Not added</jc>
064 * </p>
065 *
066 * <h5 class='section'>Behavior Notes:</h5>
067 * <ul class='spaced-list'>
068 *    <li>All list operations are delegated to the underlying list
069 *    <li>The fluent methods ({@link #a(Object)}, {@link #aa(Collection)}, {@link #ai(boolean, Object)}) return <c>this</c> for chaining
070 *    <li>If a <jk>null</jk> collection is passed to {@link #aa(Collection)}, it is treated as a no-op
071 *    <li>The underlying list is stored by reference (not copied), so modifications affect the original list
072 * </ul>
073 *
074 * <h5 class='section'>Thread Safety:</h5>
075 * <p>
076 * This class is not thread-safe unless the underlying list is thread-safe. If thread safety is required,
077 * use a thread-safe list type (e.g., {@link java.util.concurrent.CopyOnWriteArrayList}).
078 *
079 * <h5 class='section'>See Also:</h5>
080 * <ul>
081 *    <li class='link'><a class="doclink" href="../../../../../index.html#juneau-commons">Overview &gt; juneau-commons</a>
082 * </ul>
083 *
084 * @param <E> The element type.
085 */
086@SuppressWarnings({
087   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
088})
089public class FluentList<E> extends AbstractList<E> {
090
091   private final List<E> list;
092
093   /**
094    * Constructor.
095    *
096    * @param inner The underlying list to wrap. Must not be <jk>null</jk>.
097    */
098   public FluentList(List<E> inner) {
099      this.list = reqnn("inner", inner);
100   }
101
102   /**
103    * Adds a single element to this list.
104    *
105    * <p>
106    * This is a convenience method that calls {@link #add(Object)} and returns <c>this</c>
107    * for method chaining.
108    *
109    * <h5 class='section'>Example:</h5>
110    * <p class='bjava'>
111    *    FluentList&lt;String&gt; <jv>list</jv> = <jk>new</jk> FluentList&lt;&gt;(<jk>new</jk> ArrayList&lt;&gt;());
112    *    <jv>list</jv>.a(<js>"item1"</js>).a(<js>"item2"</js>);
113    * </p>
114    *
115    * @param element The element to add.
116    * @return This object for method chaining.
117    */
118   public FluentList<E> a(E element) {
119      list.add(element);
120      return this;
121   }
122
123   /**
124    * Adds all elements from the specified collection to this list.
125    *
126    * <p>
127    * This is a convenience method that calls {@link #addAll(Collection)} and returns <c>this</c>
128    * for method chaining. If the specified collection is <jk>null</jk>, this is a no-op.
129    *
130    * <h5 class='section'>Example:</h5>
131    * <p class='bjava'>
132    *    FluentList&lt;String&gt; <jv>list</jv> = <jk>new</jk> FluentList&lt;&gt;(<jk>new</jk> ArrayList&lt;&gt;());
133    *    List&lt;String&gt; <jv>other</jv> = List.of(<js>"item1"</js>, <js>"item2"</js>);
134    *    <jv>list</jv>.aa(<jv>other</jv>).a(<js>"item3"</js>);
135    * </p>
136    *
137    * @param c The collection whose elements are to be added. Can be <jk>null</jk> (no-op).
138    * @return This object for method chaining.
139    */
140   public FluentList<E> aa(Collection<? extends E> c) {
141      if (c != null)
142         list.addAll(c);
143      return this;
144   }
145
146   /**
147    * Adds an element to this list if the specified boolean condition is <jk>true</jk>.
148    *
149    * <p>
150    * This method is useful for conditionally adding elements based on runtime conditions.
151    * If the condition is <jk>false</jk>, the element is not added and this method returns <c>this</c>
152    * without modifying the list.
153    *
154    * <h5 class='section'>Example:</h5>
155    * <p class='bjava'>
156    *    <jk>boolean</jk> <jv>includeDebug</jv> = <jk>true</jk>;
157    *    <jk>boolean</jk> <jv>includeTest</jv> = <jk>false</jk>;
158    *
159    *    FluentList&lt;String&gt; <jv>list</jv> = <jk>new</jk> FluentList&lt;&gt;(<jk>new</jk> ArrayList&lt;&gt;())
160    *       .a(<js>"basic"</js>)
161    *       .ai(<jv>includeDebug</jv>, <js>"debug"</js>)   <jc>// Added</jc>
162    *       .ai(<jv>includeTest</jv>, <js>"test"</js>);    <jc>// Not added</jc>
163    * </p>
164    *
165    * @param condition The condition to evaluate. If <jk>true</jk>, the element is added; if <jk>false</jk>, it is not.
166    * @param element The element to add if the condition is <jk>true</jk>.
167    * @return This object for method chaining.
168    */
169   public FluentList<E> ai(boolean condition, E element) {
170      if (condition)
171         list.add(element);
172      return this;
173   }
174
175   @Override
176   public E get(int index) {
177      return list.get(index);
178   }
179
180   @Override
181   public int size() {
182      return list.size();
183   }
184
185   @Override
186   public boolean add(E e) {
187      return list.add(e);
188   }
189
190   @Override
191   public void add(int index, E element) {
192      list.add(index, element);
193   }
194
195   @Override
196   public boolean addAll(Collection<? extends E> c) {
197      return list.addAll(c);
198   }
199
200   @Override
201   public boolean addAll(int index, Collection<? extends E> c) {
202      return list.addAll(index, c);
203   }
204
205   @Override
206   public E remove(int index) {
207      return list.remove(index);
208   }
209
210   @Override
211   public boolean remove(Object o) {
212      return list.remove(o);
213   }
214
215   @Override
216   public boolean removeAll(Collection<?> c) {
217      return list.removeAll(c);
218   }
219
220   @Override
221   public boolean retainAll(Collection<?> c) {
222      return list.retainAll(c);
223   }
224
225   @Override
226   public void clear() {
227      list.clear();
228   }
229
230   @Override
231   public E set(int index, E element) {
232      return list.set(index, element);
233   }
234
235   @Override
236   public int indexOf(Object o) {
237      return list.indexOf(o);
238   }
239
240   @Override
241   public int lastIndexOf(Object o) {
242      return list.lastIndexOf(o);
243   }
244
245   @Override
246   public ListIterator<E> listIterator() {
247      return list.listIterator();
248   }
249
250   @Override
251   public ListIterator<E> listIterator(int index) {
252      return list.listIterator(index);
253   }
254
255   @Override
256   public List<E> subList(int fromIndex, int toIndex) {
257      return list.subList(fromIndex, toIndex);
258   }
259
260   @Override
261   public boolean contains(Object o) {
262      return list.contains(o);
263   }
264
265   @Override
266   public boolean containsAll(Collection<?> c) {
267      return list.containsAll(c);
268   }
269
270   @Override
271   public boolean isEmpty() {
272      return list.isEmpty();
273   }
274
275   @Override
276   public Iterator<E> iterator() {
277      return list.iterator();
278   }
279
280   @Override
281   public Object[] toArray() {
282      return list.toArray();
283   }
284
285   @Override
286   public <T> T[] toArray(T[] a) {
287      return list.toArray(a);
288   }
289
290   @Override
291   public String toString() {
292      return list.toString();
293   }
294
295   @Override
296   public boolean equals(Object o) {
297      return list.equals(o);
298   }
299
300   @Override
301   public int hashCode() {
302      return list.hashCode();
303   }
304}
305