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