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
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020
021import java.util.*;
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 */
086public class FluentList<E> extends AbstractList<E> {
087
088   private final List<E> list;
089
090   /**
091    * Constructor.
092    *
093    * @param inner The underlying list to wrap. Must not be <jk>null</jk>.
094    */
095   public FluentList(List<E> inner) {
096      this.list = assertArgNotNull("inner", inner);
097   }
098
099   /**
100    * Adds a single element to this list.
101    *
102    * <p>
103    * This is a convenience method that calls {@link #add(Object)} and returns <c>this</c>
104    * for method chaining.
105    *
106    * <h5 class='section'>Example:</h5>
107    * <p class='bjava'>
108    *    FluentList&lt;String&gt; <jv>list</jv> = <jk>new</jk> FluentList&lt;&gt;(<jk>new</jk> ArrayList&lt;&gt;());
109    *    <jv>list</jv>.a(<js>"item1"</js>).a(<js>"item2"</js>);
110    * </p>
111    *
112    * @param element The element to add.
113    * @return This object for method chaining.
114    */
115   public FluentList<E> a(E element) {
116      list.add(element);
117      return this;
118   }
119
120   /**
121    * Adds all elements from the specified collection to this list.
122    *
123    * <p>
124    * This is a convenience method that calls {@link #addAll(Collection)} and returns <c>this</c>
125    * for method chaining. If the specified collection is <jk>null</jk>, this is a no-op.
126    *
127    * <h5 class='section'>Example:</h5>
128    * <p class='bjava'>
129    *    FluentList&lt;String&gt; <jv>list</jv> = <jk>new</jk> FluentList&lt;&gt;(<jk>new</jk> ArrayList&lt;&gt;());
130    *    List&lt;String&gt; <jv>other</jv> = List.of(<js>"item1"</js>, <js>"item2"</js>);
131    *    <jv>list</jv>.aa(<jv>other</jv>).a(<js>"item3"</js>);
132    * </p>
133    *
134    * @param c The collection whose elements are to be added. Can be <jk>null</jk> (no-op).
135    * @return This object for method chaining.
136    */
137   public FluentList<E> aa(Collection<? extends E> c) {
138      if (c != null)
139         list.addAll(c);
140      return this;
141   }
142
143   /**
144    * Adds an element to this list if the specified boolean condition is <jk>true</jk>.
145    *
146    * <p>
147    * This method is useful for conditionally adding elements based on runtime conditions.
148    * If the condition is <jk>false</jk>, the element is not added and this method returns <c>this</c>
149    * without modifying the list.
150    *
151    * <h5 class='section'>Example:</h5>
152    * <p class='bjava'>
153    *    <jk>boolean</jk> <jv>includeDebug</jv> = <jk>true</jk>;
154    *    <jk>boolean</jk> <jv>includeTest</jv> = <jk>false</jk>;
155    *
156    *    FluentList&lt;String&gt; <jv>list</jv> = <jk>new</jk> FluentList&lt;&gt;(<jk>new</jk> ArrayList&lt;&gt;())
157    *       .a(<js>"basic"</js>)
158    *       .ai(<jv>includeDebug</jv>, <js>"debug"</js>)   <jc>// Added</jc>
159    *       .ai(<jv>includeTest</jv>, <js>"test"</js>);    <jc>// Not added</jc>
160    * </p>
161    *
162    * @param condition The condition to evaluate. If <jk>true</jk>, the element is added; if <jk>false</jk>, it is not.
163    * @param element The element to add if the condition is <jk>true</jk>.
164    * @return This object for method chaining.
165    */
166   public FluentList<E> ai(boolean condition, E element) {
167      if (condition)
168         list.add(element);
169      return this;
170   }
171
172   @Override
173   public E get(int index) {
174      return list.get(index);
175   }
176
177   @Override
178   public int size() {
179      return list.size();
180   }
181
182   @Override
183   public boolean add(E e) {
184      return list.add(e);
185   }
186
187   @Override
188   public void add(int index, E element) {
189      list.add(index, element);
190   }
191
192   @Override
193   public boolean addAll(Collection<? extends E> c) {
194      return list.addAll(c);
195   }
196
197   @Override
198   public boolean addAll(int index, Collection<? extends E> c) {
199      return list.addAll(index, c);
200   }
201
202   @Override
203   public E remove(int index) {
204      return list.remove(index);
205   }
206
207   @Override
208   public boolean remove(Object o) {
209      return list.remove(o);
210   }
211
212   @Override
213   public boolean removeAll(Collection<?> c) {
214      return list.removeAll(c);
215   }
216
217   @Override
218   public boolean retainAll(Collection<?> c) {
219      return list.retainAll(c);
220   }
221
222   @Override
223   public void clear() {
224      list.clear();
225   }
226
227   @Override
228   public E set(int index, E element) {
229      return list.set(index, element);
230   }
231
232   @Override
233   public int indexOf(Object o) {
234      return list.indexOf(o);
235   }
236
237   @Override
238   public int lastIndexOf(Object o) {
239      return list.lastIndexOf(o);
240   }
241
242   @Override
243   public ListIterator<E> listIterator() {
244      return list.listIterator();
245   }
246
247   @Override
248   public ListIterator<E> listIterator(int index) {
249      return list.listIterator(index);
250   }
251
252   @Override
253   public List<E> subList(int fromIndex, int toIndex) {
254      return list.subList(fromIndex, toIndex);
255   }
256
257   @Override
258   public boolean contains(Object o) {
259      return list.contains(o);
260   }
261
262   @Override
263   public boolean containsAll(Collection<?> c) {
264      return list.containsAll(c);
265   }
266
267   @Override
268   public boolean isEmpty() {
269      return list.isEmpty();
270   }
271
272   @Override
273   public Iterator<E> iterator() {
274      return list.iterator();
275   }
276
277   @Override
278   public Object[] toArray() {
279      return list.toArray();
280   }
281
282   @Override
283   public <T> T[] toArray(T[] a) {
284      return list.toArray(a);
285   }
286
287   @Override
288   public String toString() {
289      return list.toString();
290   }
291
292   @Override
293   public boolean equals(Object o) {
294      return list.equals(o);
295   }
296
297   @Override
298   public int hashCode() {
299      return list.hashCode();
300   }
301}
302