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 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 */ 088@SuppressWarnings({ 089 "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants 090}) 091public class FluentSet<E> extends AbstractSet<E> { 092 093 private final Set<E> set; 094 095 /** 096 * Constructor. 097 * 098 * @param inner The underlying set to wrap. Must not be <jk>null</jk>. 099 */ 100 public FluentSet(Set<E> inner) { 101 this.set = reqnn("inner", inner); 102 } 103 104 /** 105 * Adds a single element to this set. 106 * 107 * <p> 108 * This is a convenience method that calls {@link #add(Object)} and returns <c>this</c> 109 * for method chaining. If the element already exists in the set, it is not added again 110 * (sets do not allow duplicates). 111 * 112 * <h5 class='section'>Example:</h5> 113 * <p class='bjava'> 114 * FluentSet<String> <jv>set</jv> = <jk>new</jk> FluentSet<>(<jk>new</jk> LinkedHashSet<>()); 115 * <jv>set</jv>.a(<js>"item1"</js>).a(<js>"item2"</js>); 116 * </p> 117 * 118 * @param element The element to add. 119 * @return This object for method chaining. 120 */ 121 public FluentSet<E> a(E element) { 122 set.add(element); 123 return this; 124 } 125 126 /** 127 * Adds all elements from the specified collection to this set. 128 * 129 * <p> 130 * This is a convenience method that calls {@link #addAll(Collection)} and returns <c>this</c> 131 * for method chaining. If the specified collection is <jk>null</jk>, this is a no-op. 132 * Duplicate elements in the collection are automatically handled (only one occurrence is stored). 133 * 134 * <h5 class='section'>Example:</h5> 135 * <p class='bjava'> 136 * FluentSet<String> <jv>set</jv> = <jk>new</jk> FluentSet<>(<jk>new</jk> LinkedHashSet<>()); 137 * List<String> <jv>other</jv> = List.of(<js>"item1"</js>, <js>"item2"</js>); 138 * <jv>set</jv>.aa(<jv>other</jv>).a(<js>"item3"</js>); 139 * </p> 140 * 141 * @param c The collection whose elements are to be added. Can be <jk>null</jk> (no-op). 142 * @return This object for method chaining. 143 */ 144 public FluentSet<E> aa(Collection<? extends E> c) { 145 if (c != null) 146 set.addAll(c); 147 return this; 148 } 149 150 /** 151 * Adds an element to this set if the specified boolean condition is <jk>true</jk>. 152 * 153 * <p> 154 * This method is useful for conditionally adding elements based on runtime conditions. 155 * If the condition is <jk>false</jk>, the element is not added and this method returns <c>this</c> 156 * without modifying the set. 157 * 158 * <h5 class='section'>Example:</h5> 159 * <p class='bjava'> 160 * <jk>boolean</jk> <jv>includeDebug</jv> = <jk>true</jk>; 161 * <jk>boolean</jk> <jv>includeTest</jv> = <jk>false</jk>; 162 * 163 * FluentSet<String> <jv>set</jv> = <jk>new</jk> FluentSet<>(<jk>new</jk> LinkedHashSet<>()) 164 * .a(<js>"basic"</js>) 165 * .ai(<jv>includeDebug</jv>, <js>"debug"</js>) <jc>// Added</jc> 166 * .ai(<jv>includeTest</jv>, <js>"test"</js>); <jc>// Not added</jc> 167 * </p> 168 * 169 * @param condition The condition to evaluate. If <jk>true</jk>, the element is added; if <jk>false</jk>, it is not. 170 * @param element The element to add if the condition is <jk>true</jk>. 171 * @return This object for method chaining. 172 */ 173 public FluentSet<E> ai(boolean condition, E element) { 174 if (condition) 175 set.add(element); 176 return this; 177 } 178 179 @Override 180 public Iterator<E> iterator() { 181 return set.iterator(); 182 } 183 184 @Override 185 public int size() { 186 return set.size(); 187 } 188 189 @Override 190 public boolean add(E e) { 191 return set.add(e); 192 } 193 194 @Override 195 public boolean addAll(Collection<? extends E> c) { 196 return set.addAll(c); 197 } 198 199 @Override 200 public boolean remove(Object o) { 201 return set.remove(o); 202 } 203 204 @Override 205 public boolean removeAll(Collection<?> c) { 206 return set.removeAll(c); 207 } 208 209 @Override 210 public boolean retainAll(Collection<?> c) { 211 return set.retainAll(c); 212 } 213 214 @Override 215 public void clear() { 216 set.clear(); 217 } 218 219 @Override 220 public boolean contains(Object o) { 221 return set.contains(o); 222 } 223 224 @Override 225 public boolean containsAll(Collection<?> c) { 226 return set.containsAll(c); 227 } 228 229 @Override 230 public boolean isEmpty() { 231 return set.isEmpty(); 232 } 233 234 @Override 235 public Object[] toArray() { 236 return set.toArray(); 237 } 238 239 @Override 240 public <T> T[] toArray(T[] a) { 241 return set.toArray(a); 242 } 243 244 @Override 245 public String toString() { 246 return set.toString(); 247 } 248 249 @Override 250 public boolean equals(Object o) { 251 return set.equals(o); 252 } 253 254 @Override 255 public int hashCode() { 256 return set.hashCode(); 257 } 258} 259