001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import static org.apache.juneau.common.internal.StringUtils.*; 016import static org.apache.juneau.common.internal.ThrowableUtils.*; 017import static org.apache.juneau.internal.ConverterUtils.*; 018import static java.util.Collections.*; 019 020import java.lang.reflect.*; 021import java.util.*; 022 023import org.apache.juneau.collections.*; 024import org.apache.juneau.parser.*; 025 026/** 027 * Builder for sets. 028 * 029 * <h5 class='section'>See Also:</h5><ul> 030 * </ul> 031 * 032 * @param <E> Element type. 033 */ 034public final class SetBuilder<E> { 035 036 private Set<E> set; 037 private boolean unmodifiable, sparse; 038 private Comparator<E> comparator; 039 040 private Class<E> elementType; 041 private Type[] elementTypeArgs; 042 043 /** 044 * Constructor. 045 * 046 * @param elementType The element type. 047 * @param elementTypeArgs The element type generic arguments if there are any. 048 */ 049 public SetBuilder(Class<E> elementType, Type...elementTypeArgs) { 050 this.elementType = elementType; 051 this.elementTypeArgs = elementTypeArgs; 052 } 053 054 /** 055 * Constructor. 056 * 057 * @param addTo The set to add to. 058 */ 059 public SetBuilder(Set<E> addTo) { 060 this.set = addTo; 061 } 062 063 /** 064 * Builds the set. 065 * 066 * @return A set conforming to the settings on this builder. 067 */ 068 public Set<E> build() { 069 if (sparse) { 070 if (set != null && set.isEmpty()) 071 set = null; 072 } else { 073 if (set == null) 074 set = new LinkedHashSet<>(0); 075 } 076 if (set != null) { 077 if (comparator != null) { 078 Set<E> s = new TreeSet<>(comparator); 079 s.addAll(set); 080 set = s; 081 } 082 if (unmodifiable) 083 set = unmodifiableSet(set); 084 } 085 return set; 086 } 087 088 /** 089 * When specified, the {@link #build()} method will return <jk>null</jk> if the set is empty. 090 * 091 * <p> 092 * Otherwise {@link #build()} will never return <jk>null</jk>. 093 * 094 * @return This object. 095 */ 096 public SetBuilder<E> sparse() { 097 this.sparse = true; 098 return this; 099 } 100 101 /** 102 * When specified, {@link #build()} will return an unmodifiable set. 103 * 104 * @return This object. 105 */ 106 public SetBuilder<E> unmodifiable() { 107 this.unmodifiable = true; 108 return this; 109 } 110 111 /** 112 * Forces the existing set to be copied instead of appended to. 113 * 114 * @return This object. 115 */ 116 public SetBuilder<E> copy() { 117 if (set != null) 118 set = new LinkedHashSet<>(set); 119 return this; 120 } 121 122 /** 123 * Converts the set into a {@link SortedSet}. 124 * 125 * @return This object. 126 */ 127 @SuppressWarnings("unchecked") 128 public SetBuilder<E> sorted() { 129 return sorted((Comparator<E>)Comparator.naturalOrder()); 130 } 131 132 /** 133 * Converts the set into a {@link SortedSet} using the specified comparator. 134 * 135 * @param comparator The comparator to use for sorting. 136 * @return This object. 137 */ 138 public SetBuilder<E> sorted(Comparator<E> comparator) { 139 this.comparator = comparator; 140 return this; 141 } 142 143 /** 144 * Appends the contents of the specified collection into this set. 145 * 146 * <p> 147 * This is a no-op if the value is <jk>null</jk>. 148 * 149 * @param value The collection to add to this set. 150 * @return This object. 151 */ 152 public SetBuilder<E> addAll(Collection<E> value) { 153 if (value != null) { 154 if (set == null) 155 set = new LinkedHashSet<>(value); 156 else 157 set.addAll(value); 158 } 159 return this; 160 } 161 162 /** 163 * Adds a single value to this set. 164 * 165 * @param value The value to add to this set. 166 * @return This object. 167 */ 168 public SetBuilder<E> add(E value) { 169 if (set == null) 170 set = new LinkedHashSet<>(); 171 set.add(value); 172 return this; 173 } 174 175 /** 176 * Adds multiple values to this set. 177 * 178 * @param values The values to add to this set. 179 * @return This object. 180 */ 181 @SuppressWarnings("unchecked") 182 public SetBuilder<E> add(E...values) { 183 for (E v : values) 184 add(v); 185 return this; 186 } 187 188 /** 189 * Adds entries to this set via JSON array strings. 190 * 191 * @param values The JSON array strings to parse and add to this set. 192 * @return This object. 193 */ 194 public SetBuilder<E> addJson(String...values) { 195 return addAny((Object[])values); 196 } 197 198 /** 199 * Adds arbitrary values to this set. 200 * 201 * <p> 202 * Objects can be any of the following: 203 * <ul> 204 * <li>The same type or convertible to the element type of this set. 205 * <li>Collections or arrays of anything on this set. 206 * <li>JSON array strings parsed and convertible to the element type of this set. 207 * </ul> 208 * 209 * @param values The values to add. 210 * @return This object. 211 */ 212 public SetBuilder<E> addAny(Object...values) { 213 if (elementType == null) 214 throw new IllegalStateException("Unknown element type. Cannot use this method."); 215 try { 216 if (values != null) { 217 for (Object o : values) { 218 if (o != null) { 219 if (o instanceof Collection) { 220 ((Collection<?>)o).forEach(x -> addAny(x)); 221 } else if (o.getClass().isArray()) { 222 for (int i = 0; i < Array.getLength(o); i++) 223 addAny(Array.get(o, i)); 224 } else if (isJsonArray(o, false)) { 225 new JsonList(o.toString()).forEach(x -> addAny(x)); 226 } else if (elementType.isInstance(o)) { 227 add(elementType.cast(o)); 228 } else { 229 add(toType(o, elementType, elementTypeArgs)); 230 } 231 } 232 } 233 } 234 } catch (ParseException e) { 235 throw asRuntimeException(e); 236 } 237 return this; 238 } 239 240 /** 241 * Adds a value to this set if the specified flag is true. 242 * 243 * @param flag The flag. 244 * @param value The value. 245 * @return This object. 246 */ 247 public SetBuilder<E> addIf(boolean flag, E value) { 248 if (flag) 249 add(value); 250 return this; 251 } 252}