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