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