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