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