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.http.classic.part;
018
019import static org.apache.juneau.commons.utils.CollectionUtils.*;
020import static org.apache.juneau.commons.utils.PredicateUtils.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022import static org.apache.juneau.commons.utils.StringUtils.*;
023
024import java.util.*;
025import java.util.function.*;
026import java.util.stream.*;
027
028import org.apache.http.*;
029import org.apache.http.util.*;
030import org.apache.juneau.commons.svl.*;
031import org.apache.juneau.commons.utils.*;
032import org.apache.juneau.http.*;
033
034/**
035 * An simple list of HTTP parts (form-data, query-parameters, path-parameters).
036 *
037 * <h5 class='figure'>Example</h5>
038 * <p class='bjava'>
039 *    PartList <jv>parts</jv> = PartList
040 *       .<jsm>create</jsm>()
041 *       .append(MyPart.<jsm>of</jsm>(<js>"foo"</js>))
042 *       .append(<js>"Bar"</js>, ()-&gt;<jsm>getDynamicValueFromSomewhere</jsm>());
043 * </p>
044 *
045 * <h5 class='section'>See Also:</h5><ul>
046 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestCommon">juneau-rest-common</a>
047 * </ul>
048 */
049@SuppressWarnings({
050   "java:S110", // Inheritance depth acceptable for this class hierarchy
051   "java:S127", // the set(...) replace loops shrink the cached loop bound (j--) when a same-named part is removed during iteration
052   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
053   "java:S2160" // equals() inherited from ArrayList; list equality is element-based, which is correct for part lists
054})
055public class PartList extends ArrayList<NameValuePair> {
056
057   /** Represents no part list in annotations. */
058   public static final class Void extends PartList {
059      private static final long serialVersionUID = 1L;
060   }
061
062   private static final long serialVersionUID = 1L;
063
064   /**
065    * Instantiates a new part list.
066    *
067    * @return A new part list.
068    */
069   public static PartList create() {
070      return new PartList();
071   }
072
073   /**
074    * Creates a new {@link PartList} initialized with the specified parts.
075    *
076    * @param parts
077    *    The parts to add to the list.
078    *    <br>Can be <jk>null</jk>.
079    *    <br><jk>null</jk> entries are ignored.
080    * @return A new instance, never <jk>null</jk>.
081    */
082   public static PartList of(List<NameValuePair> parts) {
083      return new PartList().append(parts);
084   }
085
086   /**
087    * Creates a new {@link PartList} initialized with the specified parts.
088    *
089    * @param parts
090    *    The parts to add to the list.
091    *    <br><jk>null</jk> entries are ignored.
092    * @return A new instance, never <jk>null</jk>.
093    */
094   public static PartList of(NameValuePair...parts) {
095      return new PartList().append(parts);
096   }
097
098   /**
099    * Creates a new {@link PartList} initialized with the specified name/value pairs.
100    *
101    * <h5 class='figure'>Example</h5>
102    * <p class='bjava'>
103    *    PartList <jv>parts</jv> = PartList.<jsm>ofPairs</jsm>(<js>"foo"</js>, 1, <js>"bar"</js>, <jk>true</jk>);
104    * </p>
105    *
106    * @param pairs
107    *    Initial list of pairs.
108    *    <br>Must be an even number of parameters representing key/value pairs.
109    * @throws RuntimeException If odd number of parameters were specified.
110    * @return A new instance.
111    */
112   public static PartList ofPairs(String...pairs) {
113      var x = new PartList();
114      if (pairs == null)
115         pairs = new String[0];
116      req(pairs.length % 2 == 0, "Odd number of parameters passed into PartList.ofPairs()");
117      for (var i = 0; i < pairs.length; i += 2)
118         x.add(BasicPart.of(pairs[i], pairs[i + 1]));
119      return x;
120   }
121
122   private transient VarResolver varResolver;
123   boolean caseInsensitive;
124
125   /**
126    * Constructor.
127    */
128   public PartList() {
129      super();
130   }
131
132   /**
133    * Copy constructor.
134    *
135    * @param copyFrom The bean to copy.  Must not be <jk>null</jk>.
136    */
137   protected PartList(PartList copyFrom) {
138      super(copyFrom);
139      caseInsensitive = copyFrom.caseInsensitive;
140   }
141
142   /**
143    * Adds the specified parts to the end of the parts in this list.
144    *
145    * @param values The parts to add.  <jk>null</jk> values are ignored.
146    * @return This object.
147    */
148   public PartList append(List<NameValuePair> values) {
149      if (nn(values))
150         values.forEach(this::append);
151      return this;
152   }
153
154   /**
155    * Adds the specified part to the end of the parts in this list.
156    *
157    * @param value The part to add.  <jk>null</jk> values are ignored.
158    * @return This object.
159    */
160   public PartList append(NameValuePair value) {
161      if (nn(value))
162         add(value);
163      return this;
164   }
165
166   /**
167    * Adds the specified parts to the end of the parts in this list.
168    *
169    * @param values The parts to add.  <jk>null</jk> values are ignored.
170    * @return This object.
171    */
172   public PartList append(NameValuePair...values) {
173      if (nn(values))
174         for (var value : values)
175            append(value);
176      return this;
177   }
178
179   /**
180    * Appends the specified part to the end of this list.
181    *
182    * <p>
183    * The part is added as a {@link BasicPart}.
184    *
185    * @param name The part name.
186    * @param value The part value.
187    * @return This object.
188    */
189   public PartList append(String name, Object value) {
190      return append(createPart(name, value));
191   }
192
193   /**
194    * Appends the specified part to the end of this list using a value supplier.
195    *
196    * <p>
197    * The part is added as a {@link BasicPart}.
198    *
199    * <p>
200    * Value is re-evaluated on each call to {@link BasicPart#getValue()}.
201    *
202    * @param name The part name.
203    * @param value The part value supplier.
204    * @return This object.
205    */
206   public PartList append(String name, Supplier<?> value) {
207      return append(createPart(name, value));
208   }
209
210   /**
211    * Specifies that the parts in this list should be treated as case-sensitive.
212    *
213    * <p>
214    * The default behavior is case-sensitive.
215    *
216    * @param value The new value for this setting.
217    * @return This object.
218    */
219   public PartList caseInsensitive(boolean value) {
220      caseInsensitive = value;
221      return this;
222   }
223
224   /**
225    * Tests if parts with the given name are contained within this list.
226    *
227    * <p>
228    * Part name comparison is case insensitive.
229    *
230    * @param name The part name.
231    * @return <jk>true</jk> if at least one part with the name is present.
232    */
233   public boolean contains(String name) {
234      return stream().anyMatch(x -> eq(x.getName(), name));
235   }
236
237   /**
238    * Makes a copy of this list.
239    *
240    * @return A new copy of this list.
241    */
242   public PartList copy() {
243      return new PartList(this);
244   }
245
246   /**
247    * Performs an action on all the matching parts in this list.
248    *
249    * <p>
250    * This is the preferred method for iterating over parts as it does not involve
251    * creation or copy of lists/arrays.
252    *
253    * @param filter A predicate to apply to each element to determine if it should be included.  Can be <jk>null</jk> (if <jk>null</jk>, all elements are accepted).
254    * @param action An action to perform on each element.
255    * @return This object.
256    */
257   public PartList forEach(Predicate<NameValuePair> filter, Consumer<NameValuePair> action) {
258      forEach(x -> consumeIf(filter, action, x));
259      return this;
260   }
261
262   /**
263    * Performs an action on all parts in this list with the specified name.
264    *
265    * <p>
266    * This is the preferred method for iterating over parts as it does not involve
267    * creation or copy of lists/arrays.
268    *
269    * @param name The parts name.
270    * @param action An action to perform on each element.
271    * @return This object.
272    */
273   public PartList forEach(String name, Consumer<NameValuePair> action) {
274      return forEach(x -> eq(name, x.getName()), action);
275   }
276
277   /**
278    * Performs an action on the values for all matching parts in this list.
279    *
280    * @param filter A predicate to apply to each element to determine if it should be included.  Can be <jk>null</jk> (if <jk>null</jk>, all elements are accepted).
281    * @param action An action to perform on each element.
282    * @return This object.
283    */
284   public PartList forEachValue(Predicate<NameValuePair> filter, Consumer<String> action) {
285      return forEach(filter, x -> action.accept(x.getValue()));
286   }
287
288   /**
289    * Performs an action on the values of all matching parts in this list.
290    *
291    * @param name The part name.
292    * @param action An action to perform on each element.
293    * @return This object.
294    */
295   public PartList forEachValue(String name, Consumer<String> action) {
296      return forEach(name, x -> action.accept(x.getValue()));
297   }
298
299   /**
300    * Gets a part representing all of the part values with the given name.
301    *
302    * <p>
303    * Same as {@link #get(String, Class)} but the part name is pulled from the name or value attribute of the {@link FormData}/{@link Query}/{@link Path} annotation.
304    *
305    * <h5 class='figure'>Example</h5>
306    * <p class='bjava'>
307    *    Age <jv>age</jv> = <jv>partList</jv>.get(Age.<jk>class</jk>);
308    * </p>
309    *
310    * @param <T> The part implementation class.
311    * @param type The part implementation class.  Must not be <jk>null</jk>.
312    * @return A part with a condensed value, or an empty {@link Optional} if no parts by the given name are present.
313    */
314   public <T> Optional<T> get(Class<T> type) {
315      reqnn("type", type);
316
317      var name = PartBeanMeta.of(type).getSchema().getName();
318      req(nn(name), "Part name could not be found on bean type '%s'", cn(type));
319
320      return get(name, type);
321   }
322
323   /**
324    * Gets a part representing all of the part values with the given name.
325    *
326    * <p>
327    * If more that one part with the given name exists the values will be combined with <js>", "</js> as per
328    * <a href='https://tools.ietf.org/html/rfc2616#section-4.2'>RFC 2616 Section 4.2</a>.
329    *
330    * @param name The part name.
331    * @return A part with a condensed value, or {@link Optional#empty()} if no parts by the given name are present
332    */
333   public Optional<NameValuePair> get(String name) {
334
335      NameValuePair first = null;
336      List<NameValuePair> rest = null;
337      for (var x : this) {
338         if (eq(x.getName(), name)) {
339            if (first == null)
340               first = x;
341            else {
342               if (rest == null)
343                  rest = list();
344               rest.add(x);
345            }
346         }
347      }
348
349      if (first == null)
350         return oe();
351
352      if (rest == null)
353         return o(first);
354
355      var sb = new CharArrayBuffer(128);
356      sb.append(first.getValue());
357      for (var element : rest) {
358         sb.append(',');
359         sb.append(element.getValue());
360      }
361
362      return o(new BasicStringPart(name, sb.toString()));
363   }
364
365   /**
366    * Gets a part representing all of the part values with the given name.
367    *
368    * <p>
369    * If more that one part with the given name exists the values will be combined with <js>", "</js> as per
370    * <a href='https://tools.ietf.org/html/rfc2616#section-4.2'>RFC 2616 Section 4.2</a>.
371    *
372    * <p>
373    * The implementation class must have a public constructor taking in one of the following argument lists:
374    * <ul>
375    *    <li><c>X(String <jv>value</jv>)</c>
376    *    <li><c>X(Object <jv>value</jv>)</c>
377    *    <li><c>X(String <jv>name</jv>, String <jv>value</jv>)</c>
378    *    <li><c>X(String <jv>name</jv>, Object <jv>value</jv>)</c>
379    * </ul>
380    *
381    * <h5 class='figure'>Example</h5>
382    * <p class='bjava'>
383    *    BasicIntegerPart <jv>age</jv> = <jv>partList</jv>.get(<js>"age"</js>, BasicIntegerPart.<jk>class</jk>);
384    * </p>
385    *
386    * @param <T> The part implementation class.
387    * @param name The part name.
388    * @param type The part implementation class.
389    * @return A part with a condensed value, or an empty {@link Optional} if no parts by the given name are present.
390    */
391   public <T> Optional<T> get(String name, Class<T> type) {
392
393      NameValuePair first = null;
394      List<NameValuePair> rest = null;
395      for (var x : this) {
396         if (eq(x.getName(), name)) {
397            if (first == null)
398               first = x;
399            else {
400               if (rest == null)
401                  rest = list();
402               rest.add(x);
403            }
404         }
405      }
406
407      if (first == null)
408         return oe();
409
410      if (rest == null)
411         return o(PartBeanMeta.of(type).construct(name, first.getValue()));
412
413      var sb = new CharArrayBuffer(128);
414      sb.append(first.getValue());
415      for (var element : rest) {
416         sb.append(',');
417         sb.append(element.getValue());
418      }
419
420      return o(PartBeanMeta.of(type).construct(name, sb.toString()));
421   }
422
423   /**
424    * Gets all of the parts.
425    *
426    * <p>
427    * The returned array maintains the relative order in which the parts were added.
428    * Each call creates a new array not backed by this list.
429    *
430    * <p>
431    * As a general rule, it's more efficient to use the other methods with consumers to
432    * get parts.
433    *
434    * @return An array containing all parts, never <jk>null</jk>.
435    */
436   public NameValuePair[] getAll() { return stream().toArray(NameValuePair[]::new); }
437
438   /**
439    * Gets all of the parts with the given name.
440    *
441    * <p>
442    * The returned array maintains the relative order in which the parts were added.
443    * Part name comparison is case sensitive by default.
444    * Parts with null values are ignored.
445    * Each call creates a new array not backed by this list.
446    *
447    * <p>
448    * As a general rule, it's more efficient to use the other methods with consumers to
449    * get parts.
450    *
451    * @param name The part name.
452    *
453    * @return An array containing all matching parts, never <jk>null</jk>.
454    */
455   public NameValuePair[] getAll(String name) {
456      return stream().filter(x -> eq(x.getName(), name)).toArray(NameValuePair[]::new);
457   }
458
459   /**
460    * Gets the first part with the given name.
461    *
462    * <p>
463    * Part name comparison is case sensitive by default.
464    *
465    * @param name The part name.
466    * @return The first matching part, or {@link Optional#empty()} if not found.
467    */
468   public Optional<NameValuePair> getFirst(String name) {
469      for (var i = 0; i < size(); i++) {
470         var x = get(i);
471         if (eq(x.getName(), name))
472            return o(x);
473      }
474      return oe();
475   }
476
477   /**
478    * Gets the last part with the given name.
479    *
480    * <p>
481    * Part name comparison is case sensitive by default.
482    *
483    * @param name The part name.
484    * @return The last matching part, or {@link Optional#empty()} if not found.
485    */
486   public Optional<NameValuePair> getLast(String name) {
487      for (var i = size() - 1; i >= 0; i--) {
488         var x = get(i);
489         if (eq(x.getName(), name))
490            return o(x);
491      }
492      return oe();
493   }
494
495   /**
496    * Returns all the string values for all parts with the specified name.
497    *
498    * @param name The part name.
499    * @return An array containing all values.  Never <jk>null</jk>.
500    */
501   public String[] getValues(String name) {
502      return stream().filter(x -> eq(x.getName(), name)).map(NameValuePair::getValue).toArray(String[]::new);
503   }
504
505   /**
506    * Returns an iterator over this list of parts.
507    *
508    * @return A new iterator over this list of parts.
509    */
510   public PartIterator partIterator() {
511      return new BasicPartIterator(toArray(new NameValuePair[0]), null, caseInsensitive);
512   }
513
514   /**
515    * Returns an iterator over the parts with a given name in this list.
516    *
517    * @param name The name of the parts over which to iterate, or <jk>null</jk> for all parts
518    *
519    * @return A new iterator over the matching parts in this list.
520    */
521   public PartIterator partIterator(String name) {
522      return new BasicPartIterator(getAll(name), name, caseInsensitive);
523   }
524
525   /**
526    * Adds the specified parts to the beginning of the parts in this list.
527    *
528    * @param values The parts to add.  <jk>null</jk> values are ignored.
529    * @return This object.
530    */
531   public PartList prepend(List<NameValuePair> values) {
532      if (nn(values))
533         addAll(0, values);
534      return this;
535   }
536
537   /**
538    * Adds the specified part to the beginning of the parts in this list.
539    *
540    * @param value The part to add.  <jk>null</jk> values are ignored.
541    * @return This object.
542    */
543   public PartList prepend(NameValuePair value) {
544      if (nn(value))
545         add(0, value);
546      return this;
547   }
548
549   /**
550    * Adds the specified parts to the beginning of the parts in this list.
551    *
552    * @param values The parts to add.  <jk>null</jk> values are ignored.
553    * @return This object.
554    */
555   public PartList prepend(NameValuePair...values) {
556      if (nn(values))
557         prepend(l(values));
558      return this;
559   }
560
561   /**
562    * Appends the specified part to the beginning of this list.
563    *
564    * <p>
565    * The part is added as a {@link BasicPart}.
566    *
567    * @param name The part name.
568    * @param value The part value.
569    * @return This object.
570    */
571   public PartList prepend(String name, Object value) {
572      return prepend(createPart(name, value));
573   }
574
575   /**
576    * Appends the specified part to the beginning of this list using a value supplier.
577    *
578    * <p>
579    * The part is added as a {@link BasicPart}.
580    *
581    * <p>
582    * Value is re-evaluated on each call to {@link BasicPart#getValue()}.
583    *
584    * @param name The part name.
585    * @param value The part value supplier.
586    * @return This object.
587    */
588   public PartList prepend(String name, Supplier<?> value) {
589      return prepend(createPart(name, value));
590   }
591
592   /**
593    * Removes the specified parts from this list.
594    *
595    * @param values The parts to remove.  <jk>null</jk> values are ignored.
596    * @return This object.
597    */
598   public PartList remove(List<NameValuePair> values) {
599      if (nn(values))
600         values.forEach(this::remove);
601      return this;
602   }
603
604   /**
605    * Removes the specified part from this list.
606    *
607    * @param value The part to remove.  <jk>null</jk> values are ignored.
608    * @return This object.
609    */
610   public PartList remove(NameValuePair value) {
611      if (nn(value))
612         removeIf(x -> eq(x.getName(), value.getName()) && eq(x.getValue(), value.getValue()));
613      return this;
614   }
615
616   /**
617    * Removes the specified parts from this list.
618    *
619    * @param values The parts to remove.  <jk>null</jk> values are ignored.
620    * @return This object.
621    */
622   public PartList remove(NameValuePair...values) {
623      for (var value : values)
624         remove(value);
625      return this;
626   }
627
628   /**
629    * Removes the part with the specified name from this list.
630    *
631    * @param name The part name.
632    * @return This object.
633    */
634   public PartList remove(String name) {
635      removeIf(x -> eq(x.getName(), name));
636      return this;
637   }
638
639   /**
640    * Removes the part with the specified name from this list.
641    *
642    * @param names The part name.
643    * @return This object.
644    */
645   public PartList remove(String...names) {
646      if (nn(names))
647         for (var name : names)
648            remove(name);
649      return this;
650   }
651
652   /**
653    * Allows part values to contain SVL variables.
654    *
655    * <p>
656    * Resolves variables in part values when using the following methods:
657    * <ul>
658    *    <li class='jm'>{@link #append(String, Object) append(String,Object)}
659    *    <li class='jm'>{@link #append(String, Supplier) append(String,Supplier&lt;?&gt;)}
660    *    <li class='jm'>{@link #prepend(String, Object) prepend(String,Object)}
661    *    <li class='jm'>{@link #prepend(String, Supplier) prepend(String,Supplier&lt;?&gt;)}
662    *    <li class='jm'>{@link #set(String, Object) set(String,Object)}
663    *    <li class='jm'>{@link #set(String, Supplier) set(String,Supplier&lt;?&gt;)}
664    * </ul>
665    *
666    * <p>
667    * Uses {@link VarResolver#DEFAULT} to resolve variables.
668    *
669    * @return This object.
670    */
671   public PartList resolving() {
672      return resolving(VarResolver.DEFAULT);
673   }
674
675   /**
676    * Allows part values to contain SVL variables.
677    *
678    * <p>
679    * Resolves variables in part values when using the following methods:
680    * <ul>
681    *    <li class='jm'>{@link #append(String, Object) append(String,Object)}
682    *    <li class='jm'>{@link #append(String, Supplier) append(String,Supplier&lt;?&gt;)}
683    *    <li class='jm'>{@link #prepend(String, Object) prepend(String,Object)}
684    *    <li class='jm'>{@link #prepend(String, Supplier) prepend(String,Supplier&lt;?&gt;)}
685    *    <li class='jm'>{@link #set(String, Object) set(String,Object)}
686    *    <li class='jm'>{@link #set(String, Supplier) set(String,Supplier&lt;?&gt;)}
687    * </ul>
688    *
689    * @param varResolver The variable resolver to use for resolving variables.  Can be <jk>null</jk> (disables variable resolution).
690    * @return This object.
691    */
692   public PartList resolving(VarResolver varResolver) {
693      this.varResolver = varResolver;
694      return this;
695   }
696
697   /**
698    * Replaces the first occurrence of the parts with the same name.
699    *
700    * <p>
701    * If no part with the same name is found the given part is added to the end of the list.
702    *
703    * @param values The parts to replace.  <jk>null</jk> values are ignored.
704    * @return This object.
705    */
706   @SuppressWarnings({
707      "java:S3776" // Cognitive complexity acceptable for this specific logic
708   })
709   public PartList set(List<NameValuePair> values) {
710
711      if (nn(values)) {
712         for (var h : values) {
713            if (nn(h)) {
714               for (int i2 = 0, j2 = size(); i2 < j2; i2++) {
715                  var x = get(i2);
716                  if (eq(x.getName(), h.getName())) {
717                     remove(i2);
718                     j2--;
719                  }
720               }
721            }
722         }
723
724         for (var x : values) {
725            if (nn(x)) {
726               add(x);
727            }
728         }
729      }
730
731      return this;
732   }
733
734   /**
735    * Adds or replaces the part(s) with the same name.
736    *
737    * <p>
738    * If no part with the same name is found the given part is added to the end of the list.
739    *
740    * @param value The part to replace.  <jk>null</jk> values are ignored.
741    * @return This object.
742    */
743   public PartList set(NameValuePair value) {
744      if (nn(value)) {
745         var replaced = false;
746         for (int i = 0, j = size(); i < j; i++) {
747            var x = get(i);
748            if (eq(x.getName(), value.getName())) {
749               if (replaced) {
750                  remove(i);
751                  j--;
752               } else {
753                  set(i, value);
754                  replaced = true;
755               }
756            }
757         }
758
759         if (! replaced)
760            add(value);
761      }
762
763      return this;
764   }
765
766   /**
767    * Adds or replaces the part(s) with the same name.
768    *
769    * <p>
770    * If no part with the same name is found the given part is added to the end of the list.
771    *
772    * @param values The part to replace.  <jk>null</jk> values are ignored.
773    * @return This object.
774    */
775   public PartList set(NameValuePair...values) {
776      if (nn(values))
777         set(l(values));
778      return this;
779   }
780
781   /**
782    * Replaces the first occurrence of the parts with the same name.
783    *
784    * @param name The part name.
785    * @param value The part value.
786    * @return This object.
787    */
788   public PartList set(String name, Object value) {
789      return set(createPart(name, value));
790   }
791
792   /**
793    * Replaces the first occurrence of the parts with the same name.
794    *
795    * @param name The part name.
796    * @param value The part value.
797    * @return This object.
798    */
799   public PartList set(String name, Supplier<?> value) {
800      return set(createPart(name, value));
801   }
802
803   /**
804    * Adds a collection of default parts.
805    *
806    * <p>
807    * Default parts are set if they're not already in the list.
808    *
809    * @param parts The list of default parts.
810    * @return This object.
811    */
812   public PartList setDefault(List<NameValuePair> parts) {
813      if (nn(parts))
814         parts.stream().filter(x -> nn(x) && ! contains(x.getName())).forEach(this::set);
815      return this;
816   }
817
818   /**
819    * Makes a copy of this list of parts and adds a collection of default parts.
820    *
821    * <p>
822    * Default parts are set if they're not already in the list.
823    *
824    * @param parts The list of default parts.
825    * @return A new list, or the same list if the parts were empty.
826    */
827   public PartList setDefault(NameValuePair...parts) {
828      if (nn(parts))
829         setDefault(l(parts));
830      return this;
831   }
832
833   /**
834    * Replaces the first occurrence of the part with the same name.
835    *
836    * @param name The header name.
837    * @param value The header value.
838    * @return This object.
839    */
840   public PartList setDefault(String name, Object value) {
841      return setDefault(createPart(name, value));
842   }
843
844   /**
845    * Replaces the first occurrence of the headers with the same name.
846    *
847    * @param name The header name.
848    * @param value The header value.
849    * @return This object.
850    */
851   public PartList setDefault(String name, Supplier<?> value) {
852      return setDefault(createPart(name, value));
853   }
854
855   /**
856    * Returns an unmodifiable snapshot of this list.
857    *
858    * <p>
859    * Idempotency: if this list is already an {@link Unmodifiable} snapshot, it is returned as-is.
860    *
861    * @return An unmodifiable snapshot of this list, or this list if it is already unmodifiable.
862    */
863   public PartList unmodifiable() {
864      return this instanceof Unmodifiable ? this : new Unmodifiable(this);
865   }
866
867   /**
868    * Returns <jk>true</jk> if this list is an unmodifiable snapshot.
869    *
870    * @return <jk>true</jk> if this list is an {@link Unmodifiable} snapshot.
871    */
872   public boolean isUnmodifiable() {
873      return this instanceof UnmodifiableBean;
874   }
875
876   /**
877    * Returns a stream of the parts in this list with the specified name.
878    *
879    * <p>
880    * This does not involve a copy of the underlying array of <c>NameValuePair</c> objects so should perform well.
881    *
882    * @param name The part name.
883    * @return This object.
884    */
885   public Stream<NameValuePair> stream(String name) {
886      return stream().filter(x -> eq(name, x.getName()));
887   }
888
889   /**
890    * Returns this list as a URL-encoded custom query.
891    */
892   @Override /* Overridden from Object */
893   public String toString() {
894      var sb = new StringBuilder();
895      forEach(p -> {
896         if (nn(p)) {
897            var v = p.getValue();
898            if (nn(v)) {
899               if (!sb.isEmpty())
900                  sb.append("&");
901               sb.append(urlEncode(p.getName())).append('=').append(urlEncode(p.getValue()));
902            }
903         }
904      });
905      return sb.toString();
906   }
907
908   private NameValuePair createPart(String name, Object value) {
909      var isResolving = nn(varResolver);
910
911      if (value instanceof Supplier<?> value2) {
912         return new BasicPart(name, isResolving ? resolver(value2) : value2);
913      }
914      return new BasicPart(name, isResolving ? resolver(value) : value);
915   }
916
917   private boolean eq(String s1, String s2) {
918      return Shorts.eq(caseInsensitive, s1, s2);  // NOAI
919   }
920
921   private Supplier<Object> resolver(Object input) {
922      return () -> varResolver.resolve(s(unwrap(input)));
923   }
924
925   private static Object unwrap(Object o) {
926      while (o instanceof Supplier<?> s)
927         o = s.get();
928      return o;
929   }
930
931   /**
932    * An unmodifiable snapshot of a {@link PartList}.
933    *
934    * <p>
935    * This is the "collection-mutator-override" variant of the immutability paradigm.  Because {@link PartList}
936    * extends {@link ArrayList}, a single {@code modify()} funnel cannot intercept the inherited {@code ArrayList}
937    * mutators, so this subclass snapshot-copies the elements in its constructor and overrides the entire
938    * collection-mutator surface — its own fluent mutators plus the inherited {@code ArrayList} mutators (including the
939    * {@code iterator()} / {@code listIterator()} mutators) — to throw {@link UnsupportedOperationException}.  Read
940    * operations and non-mutating iteration continue to work.
941    */
942   public static class Unmodifiable extends PartList implements UnmodifiableBean {
943
944      private static final long serialVersionUID = 1L;
945
946      /**
947       * Constructor.
948       *
949       * @param copyFrom The list to snapshot-copy.  Must not be <jk>null</jk>.
950       */
951      Unmodifiable(PartList copyFrom) {
952         super(copyFrom);
953      }
954
955      // --- Fluent mutators -----------------------------------------------------------------------------------------
956
957      @Override public PartList append(List<NameValuePair> values) { throw uoex(RO); }
958      @Override public PartList append(NameValuePair value) { throw uoex(RO); }
959      @Override public PartList append(NameValuePair...values) { throw uoex(RO); }
960      @Override public PartList append(String name, Object value) { throw uoex(RO); }
961      @Override public PartList append(String name, Supplier<?> value) { throw uoex(RO); }
962      @Override public PartList caseInsensitive(boolean value) { throw uoex(RO); }
963      @Override public PartList prepend(List<NameValuePair> values) { throw uoex(RO); }
964      @Override public PartList prepend(NameValuePair value) { throw uoex(RO); }
965      @Override public PartList prepend(NameValuePair...values) { throw uoex(RO); }
966      @Override public PartList prepend(String name, Object value) { throw uoex(RO); }
967      @Override public PartList prepend(String name, Supplier<?> value) { throw uoex(RO); }
968      @Override public PartList remove(List<NameValuePair> values) { throw uoex(RO); }
969      @Override public PartList remove(NameValuePair value) { throw uoex(RO); }
970      @Override public PartList remove(NameValuePair...values) { throw uoex(RO); }
971      @Override public PartList remove(String name) { throw uoex(RO); }
972      @Override public PartList remove(String...names) { throw uoex(RO); }
973      @Override public PartList resolving() { throw uoex(RO); }
974      @Override public PartList resolving(VarResolver varResolver) { throw uoex(RO); }
975      @Override public PartList set(List<NameValuePair> values) { throw uoex(RO); }
976      @Override public PartList set(NameValuePair value) { throw uoex(RO); }
977      @Override public PartList set(NameValuePair...values) { throw uoex(RO); }
978      @Override public PartList set(String name, Object value) { throw uoex(RO); }
979      @Override public PartList set(String name, Supplier<?> value) { throw uoex(RO); }
980      @Override public PartList setDefault(List<NameValuePair> parts) { throw uoex(RO); }
981      @Override public PartList setDefault(NameValuePair...parts) { throw uoex(RO); }
982      @Override public PartList setDefault(String name, Object value) { throw uoex(RO); }
983      @Override public PartList setDefault(String name, Supplier<?> value) { throw uoex(RO); }
984
985      // --- Inherited ArrayList collection mutators -----------------------------------------------------------------
986
987      @Override public boolean add(NameValuePair e) { throw uoex(RO); }
988      @Override public void add(int index, NameValuePair element) { throw uoex(RO); }
989      @Override public boolean addAll(Collection<? extends NameValuePair> c) { throw uoex(RO); }
990      @Override public boolean addAll(int index, Collection<? extends NameValuePair> c) { throw uoex(RO); }
991      @Override public void clear() { throw uoex(RO); }
992      @Override public boolean remove(Object o) { throw uoex(RO); }
993      @Override public NameValuePair remove(int index) { throw uoex(RO); }
994      @Override public boolean removeAll(Collection<?> c) { throw uoex(RO); }
995      @Override public boolean removeIf(Predicate<? super NameValuePair> filter) { throw uoex(RO); }
996      @Override public void replaceAll(UnaryOperator<NameValuePair> operator) { throw uoex(RO); }
997      @Override public boolean retainAll(Collection<?> c) { throw uoex(RO); }
998      @Override public NameValuePair set(int index, NameValuePair element) { throw uoex(RO); }
999      @Override public void sort(Comparator<? super NameValuePair> c) { throw uoex(RO); }
1000
1001      @Override public Iterator<NameValuePair> iterator() {
1002         var i = super.iterator();
1003         return new Iterator<>() {
1004            @Override public boolean hasNext() { return i.hasNext(); }
1005            @Override public NameValuePair next() { return i.next(); }
1006            @Override public void remove() { throw uoex(RO); }
1007         };
1008      }
1009
1010      @Override public ListIterator<NameValuePair> listIterator() { return listIterator(0); }
1011
1012      @Override public ListIterator<NameValuePair> listIterator(int index) {
1013         var i = super.listIterator(index);
1014         return new ListIterator<>() {
1015            @Override public boolean hasNext() { return i.hasNext(); }
1016            @Override public NameValuePair next() { return i.next(); }
1017            @Override public boolean hasPrevious() { return i.hasPrevious(); }
1018            @Override public NameValuePair previous() { return i.previous(); }
1019            @Override public int nextIndex() { return i.nextIndex(); }
1020            @Override public int previousIndex() { return i.previousIndex(); }
1021            @Override public void add(NameValuePair e) { throw uoex(RO); }
1022            @Override public void set(NameValuePair e) { throw uoex(RO); }
1023            @Override public void remove() { throw uoex(RO); }
1024         };
1025      }
1026   }
1027
1028   private static final String RO = "Bean is unmodifiable.";
1029}