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}