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.internal; 018 019import static java.util.Collections.*; 020import static org.apache.juneau.common.utils.StringUtils.*; 021import static org.apache.juneau.common.utils.ThrowableUtils.*; 022import static org.apache.juneau.common.utils.Utils.*; 023import static org.apache.juneau.internal.ConverterUtils.*; 024 025import java.lang.reflect.*; 026import java.util.*; 027 028import org.apache.juneau.collections.*; 029import org.apache.juneau.parser.*; 030 031/** 032 * Builder for sets. 033 * 034 * <h5 class='section'>See Also:</h5><ul> 035 * </ul> 036 * 037 * @param <E> Element type. 038 */ 039public class SetBuilder<E> { 040 041 private Set<E> set; 042 private boolean unmodifiable, sparse; 043 private Comparator<E> comparator; 044 045 private Class<E> elementType; 046 private Type[] elementTypeArgs; 047 048 /** 049 * Constructor. 050 * 051 * @param elementType The element type. 052 * @param elementTypeArgs The element type generic arguments if there are any. 053 */ 054 public SetBuilder(Class<E> elementType, Type...elementTypeArgs) { 055 this.elementType = elementType; 056 this.elementTypeArgs = elementTypeArgs; 057 } 058 059 /** 060 * Constructor. 061 * 062 * @param addTo The set to add to. 063 */ 064 public SetBuilder(Set<E> addTo) { 065 this.set = addTo; 066 } 067 068 /** 069 * Specifies the element type on this list. 070 * 071 * @param value The element type. 072 * @return This object. 073 */ 074 public SetBuilder<E> elementType(Class<E> value) { 075 this.elementType = value; 076 return this; 077 } 078 079 /** 080 * Builds the set. 081 * 082 * @return A set conforming to the settings on this builder. 083 */ 084 public Set<E> build() { 085 if (sparse) { 086 if (set != null && set.isEmpty()) 087 set = null; 088 } else { 089 if (set == null) 090 set = new LinkedHashSet<>(0); 091 } 092 if (set != null) { 093 if (comparator != null) { 094 Set<E> s = new TreeSet<>(comparator); 095 s.addAll(set); 096 set = s; 097 } 098 if (unmodifiable) 099 set = unmodifiableSet(set); 100 } 101 return set; 102 } 103 104 /** 105 * When specified, the {@link #build()} method will return <jk>null</jk> if the set is empty. 106 * 107 * <p> 108 * Otherwise {@link #build()} will never return <jk>null</jk>. 109 * 110 * @return This object. 111 */ 112 public SetBuilder<E> sparse() { 113 this.sparse = true; 114 return this; 115 } 116 117 /** 118 * When specified, {@link #build()} will return an unmodifiable set. 119 * 120 * @return This object. 121 */ 122 public SetBuilder<E> unmodifiable() { 123 this.unmodifiable = true; 124 return this; 125 } 126 127 /** 128 * Forces the existing set to be copied instead of appended to. 129 * 130 * @return This object. 131 */ 132 public SetBuilder<E> copy() { 133 if (set != null) 134 set = new LinkedHashSet<>(set); 135 return this; 136 } 137 138 /** 139 * Converts the set into a {@link SortedSet}. 140 * 141 * @return This object. 142 */ 143 @SuppressWarnings("unchecked") 144 public SetBuilder<E> sorted() { 145 return sorted((Comparator<E>)Comparator.naturalOrder()); 146 } 147 148 /** 149 * Converts the set into a {@link SortedSet} using the specified comparator. 150 * 151 * @param comparator The comparator to use for sorting. 152 * @return This object. 153 */ 154 public SetBuilder<E> sorted(Comparator<E> comparator) { 155 this.comparator = comparator; 156 return this; 157 } 158 159 /** 160 * Appends the contents of the specified collection into this set. 161 * 162 * <p> 163 * This is a no-op if the value is <jk>null</jk>. 164 * 165 * @param value The collection to add to this set. 166 * @return This object. 167 */ 168 public SetBuilder<E> addAll(Collection<E> value) { 169 if (value != null) { 170 if (set == null) 171 set = new LinkedHashSet<>(value); 172 else 173 set.addAll(value); 174 } 175 return this; 176 } 177 178 /** 179 * Adds a single value to this set. 180 * 181 * @param value The value to add to this set. 182 * @return This object. 183 */ 184 public SetBuilder<E> add(E value) { 185 if (set == null) 186 set = new LinkedHashSet<>(); 187 set.add(value); 188 return this; 189 } 190 191 /** 192 * Adds multiple values to this set. 193 * 194 * @param values The values to add to this set. 195 * @return This object. 196 */ 197 @SuppressWarnings("unchecked") 198 public SetBuilder<E> add(E...values) { 199 for (E v : values) 200 add(v); 201 return this; 202 } 203 204 /** 205 * Adds entries to this set via JSON array strings. 206 * 207 * @param values The JSON array strings to parse and add to this set. 208 * @return This object. 209 */ 210 public SetBuilder<E> addJson(String...values) { 211 return addAny((Object[])values); 212 } 213 214 /** 215 * Adds arbitrary values to this set. 216 * 217 * <p> 218 * Objects can be any of the following: 219 * <ul> 220 * <li>The same type or convertible to the element type of this set. 221 * <li>Collections or arrays of anything on this set. 222 * <li>JSON array strings parsed and convertible to the element type of this set. 223 * </ul> 224 * 225 * @param values The values to add. 226 * @return This object. 227 */ 228 public SetBuilder<E> addAny(Object...values) { 229 if (elementType == null) 230 throw new IllegalStateException("Unknown element type. Cannot use this method."); 231 try { 232 if (values != null) { 233 for (Object o : values) { 234 if (o != null) { 235 if (o instanceof Collection) { 236 ((Collection<?>)o).forEach(x -> addAny(x)); 237 } else if (isArray(o)) { 238 for (int i = 0; i < Array.getLength(o); i++) 239 addAny(Array.get(o, i)); 240 } else if (isJsonArray(o, false)) { 241 new JsonList(o.toString()).forEach(x -> addAny(x)); 242 } else if (elementType.isInstance(o)) { 243 add(elementType.cast(o)); 244 } else { 245 add(toType(o, elementType, elementTypeArgs)); 246 } 247 } 248 } 249 } 250 } catch (ParseException e) { 251 throw asRuntimeException(e); 252 } 253 return this; 254 } 255 256 /** 257 * Adds a value to this set if the specified flag is true. 258 * 259 * @param flag The flag. 260 * @param value The value. 261 * @return This object. 262 */ 263 public SetBuilder<E> addIf(boolean flag, E value) { 264 if (flag) 265 add(value); 266 return this; 267 } 268}