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 set that provides convenient methods for adding elements. 025 * 026 * <p> 027 * This class wraps an underlying set and provides a fluent API for adding elements. All methods return 028 * <c>this</c> to allow method chaining. The underlying set can be any {@link Set} 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 Set Support:</b> Works with any set implementation 034 * <li><b>Conditional Adding:</b> Add elements conditionally based on boolean expressions 035 * <li><b>Transparent Interface:</b> Implements the full {@link Set} interface, so it can be used anywhere a set is expected 036 * <li><b>Automatic Deduplication:</b> Duplicate elements are automatically handled by the underlying set 037 * </ul> 038 * 039 * <h5 class='section'>Usage:</h5> 040 * <p class='bjava'> 041 * <jc>// Create a FluentSet wrapping a LinkedHashSet</jc> 042 * FluentSet<String> <jv>set</jv> = <jk>new</jk> FluentSet<>(<jk>new</jk> LinkedHashSet<>()); 043 * 044 * <jv>set</jv> 045 * .a(<js>"item1"</js>) 046 * .a(<js>"item2"</js>) 047 * .ai(<jk>true</jk>, <js>"item3"</js>) <jc>// Added</jc> 048 * .ai(<jk>false</jk>, <js>"item4"</js>); <jc>// Not added</jc> 049 * 050 * <jc>// Add all elements from another collection</jc> 051 * Set<String> <jv>other</jv> = Set.of(<js>"item5"</js>, <js>"item6"</js>); 052 * <jv>set</jv>.aa(<jv>other</jv>); 053 * </p> 054 * 055 * <h5 class='section'>Example - Conditional Building:</h5> 056 * <p class='bjava'> 057 * <jk>boolean</jk> <jv>includeDebug</jv> = <jk>true</jk>; 058 * <jk>boolean</jk> <jv>includeTest</jv> = <jk>false</jk>; 059 * 060 * FluentSet<String> <jv>set</jv> = <jk>new</jk> FluentSet<>(<jk>new</jk> LinkedHashSet<>()) 061 * .a(<js>"setting1"</js>) 062 * .a(<js>"setting2"</js>) 063 * .ai(<jv>includeDebug</jv>, <js>"debug"</js>) <jc>// Added</jc> 064 * .ai(<jv>includeTest</jv>, <js>"test"</js>); <jc>// Not added</jc> 065 * </p> 066 * 067 * <h5 class='section'>Behavior Notes:</h5> 068 * <ul class='spaced-list'> 069 * <li>All set operations are delegated to the underlying set 070 * <li>The fluent methods ({@link #a(Object)}, {@link #aa(Collection)}, {@link #ai(boolean, Object)}) return <c>this</c> for chaining 071 * <li>If a <jk>null</jk> collection is passed to {@link #aa(Collection)}, it is treated as a no-op 072 * <li>The underlying set is stored by reference (not copied), so modifications affect the original set 073 * <li>Duplicate elements are automatically handled by the underlying set (only one occurrence is stored) 074 * </ul> 075 * 076 * <h5 class='section'>Thread Safety:</h5> 077 * <p> 078 * This class is not thread-safe unless the underlying set is thread-safe. If thread safety is required, 079 * use a thread-safe set type (e.g., {@link java.util.concurrent.CopyOnWriteArraySet}). 080 * 081 * <h5 class='section'>See Also:</h5> 082 * <ul> 083 * <li class='link'><a class="doclink" href="../../../../../index.html#juneau-commons">Overview > juneau-commons</a> 084 * </ul> 085 * 086 * @param <E> The element type. 087 */ 088public class FluentSet<E> extends AbstractSet<E> { 089 090 private final Set<E> set; 091 092 /** 093 * Constructor. 094 * 095 * @param inner The underlying set to wrap. Must not be <jk>null</jk>. 096 */ 097 public FluentSet(Set<E> inner) { 098 this.set = assertArgNotNull("inner", inner); 099 } 100 101 /** 102 * Adds a single element to this set. 103 * 104 * <p> 105 * This is a convenience method that calls {@link #add(Object)} and returns <c>this</c> 106 * for method chaining. If the element already exists in the set, it is not added again 107 * (sets do not allow duplicates). 108 * 109 * <h5 class='section'>Example:</h5> 110 * <p class='bjava'> 111 * FluentSet<String> <jv>set</jv> = <jk>new</jk> FluentSet<>(<jk>new</jk> LinkedHashSet<>()); 112 * <jv>set</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 FluentSet<E> a(E element) { 119 set.add(element); 120 return this; 121 } 122 123 /** 124 * Adds all elements from the specified collection to this set. 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 * Duplicate elements in the collection are automatically handled (only one occurrence is stored). 130 * 131 * <h5 class='section'>Example:</h5> 132 * <p class='bjava'> 133 * FluentSet<String> <jv>set</jv> = <jk>new</jk> FluentSet<>(<jk>new</jk> LinkedHashSet<>()); 134 * List<String> <jv>other</jv> = List.of(<js>"item1"</js>, <js>"item2"</js>); 135 * <jv>set</jv>.aa(<jv>other</jv>).a(<js>"item3"</js>); 136 * </p> 137 * 138 * @param c The collection whose elements are to be added. Can be <jk>null</jk> (no-op). 139 * @return This object for method chaining. 140 */ 141 public FluentSet<E> aa(Collection<? extends E> c) { 142 if (c != null) 143 set.addAll(c); 144 return this; 145 } 146 147 /** 148 * Adds an element to this set if the specified boolean condition is <jk>true</jk>. 149 * 150 * <p> 151 * This method is useful for conditionally adding elements based on runtime conditions. 152 * If the condition is <jk>false</jk>, the element is not added and this method returns <c>this</c> 153 * without modifying the set. 154 * 155 * <h5 class='section'>Example:</h5> 156 * <p class='bjava'> 157 * <jk>boolean</jk> <jv>includeDebug</jv> = <jk>true</jk>; 158 * <jk>boolean</jk> <jv>includeTest</jv> = <jk>false</jk>; 159 * 160 * FluentSet<String> <jv>set</jv> = <jk>new</jk> FluentSet<>(<jk>new</jk> LinkedHashSet<>()) 161 * .a(<js>"basic"</js>) 162 * .ai(<jv>includeDebug</jv>, <js>"debug"</js>) <jc>// Added</jc> 163 * .ai(<jv>includeTest</jv>, <js>"test"</js>); <jc>// Not added</jc> 164 * </p> 165 * 166 * @param condition The condition to evaluate. If <jk>true</jk>, the element is added; if <jk>false</jk>, it is not. 167 * @param element The element to add if the condition is <jk>true</jk>. 168 * @return This object for method chaining. 169 */ 170 public FluentSet<E> ai(boolean condition, E element) { 171 if (condition) 172 set.add(element); 173 return this; 174 } 175 176 @Override 177 public Iterator<E> iterator() { 178 return set.iterator(); 179 } 180 181 @Override 182 public int size() { 183 return set.size(); 184 } 185 186 @Override 187 public boolean add(E e) { 188 return set.add(e); 189 } 190 191 @Override 192 public boolean addAll(Collection<? extends E> c) { 193 return set.addAll(c); 194 } 195 196 @Override 197 public boolean remove(Object o) { 198 return set.remove(o); 199 } 200 201 @Override 202 public boolean removeAll(Collection<?> c) { 203 return set.removeAll(c); 204 } 205 206 @Override 207 public boolean retainAll(Collection<?> c) { 208 return set.retainAll(c); 209 } 210 211 @Override 212 public void clear() { 213 set.clear(); 214 } 215 216 @Override 217 public boolean contains(Object o) { 218 return set.contains(o); 219 } 220 221 @Override 222 public boolean containsAll(Collection<?> c) { 223 return set.containsAll(c); 224 } 225 226 @Override 227 public boolean isEmpty() { 228 return set.isEmpty(); 229 } 230 231 @Override 232 public Object[] toArray() { 233 return set.toArray(); 234 } 235 236 @Override 237 public <T> T[] toArray(T[] a) { 238 return set.toArray(a); 239 } 240 241 @Override 242 public String toString() { 243 return set.toString(); 244 } 245 246 @Override 247 public boolean equals(Object o) { 248 return set.equals(o); 249 } 250 251 @Override 252 public int hashCode() { 253 return set.hashCode(); 254 } 255} 256