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