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