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<String> <jv>list</jv> = <jk>new</jk> FluentList<>(<jk>new</jk> ArrayList<>()); 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<String> <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<String> <jv>config</jv> = <jk>new</jk> FluentList<>(<jk>new</jk> ArrayList<>()) 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 > 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<String> <jv>list</jv> = <jk>new</jk> FluentList<>(<jk>new</jk> ArrayList<>()); 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<String> <jv>list</jv> = <jk>new</jk> FluentList<>(<jk>new</jk> ArrayList<>()); 130 * List<String> <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<String> <jv>list</jv> = <jk>new</jk> FluentList<>(<jk>new</jk> ArrayList<>()) 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