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}