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