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 RuntimeException("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(x -> set(x)); 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 (int i = 0; i < values.length; i++) 325 append(values[i]); 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(x -> append(x)); 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 (int i = 0; i < values.length; i++) 434 remove(values[i]); 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(x -> remove(x)); 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 (int i = 0; i < names.length; i++) 473 remove(names[i]); 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 (int i1 = 0, j1 = values.size(); i1 < j1; i1++) { 562 NameValuePair h = values.get(i1); 563 if (h != null) { 564 for (int i2 = 0, j2 = size(); i2 < j2; i2++) { 565 NameValuePair x = get(i2); 566 if (eq(x.getName(), h.getName())) { 567 remove(i2); 568 j2--; 569 } 570 } 571 } 572 } 573 574 for (int i = 0, j = values.size(); i < j; i++) { 575 NameValuePair x = values.get(i); 576 if (x != null) { 577 add(x); 578 } 579 } 580 } 581 582 return this; 583 } 584 585 /** 586 * Gets the first part with the given name. 587 * 588 * <p> 589 * Part name comparison is case sensitive by default. 590 * 591 * @param name The part name. 592 * @return The first matching part, or {@link Optional#empty()} if not found. 593 */ 594 public Optional<NameValuePair> getFirst(String name) { 595 for (int i = 0; i < size(); i++) { 596 NameValuePair x = get(i); 597 if (eq(x.getName(), name)) 598 return optional(x); 599 } 600 return empty(); 601 } 602 603 /** 604 * Gets the last part with the given name. 605 * 606 * <p> 607 * Part name comparison is case sensitive by default. 608 * 609 * @param name The part name. 610 * @return The last matching part, or {@link Optional#empty()} if not found. 611 */ 612 public Optional<NameValuePair> getLast(String name) { 613 for (int i = size() - 1; i >= 0; i--) { 614 NameValuePair x = get(i); 615 if (eq(x.getName(), name)) 616 return optional(x); 617 } 618 return empty(); 619 } 620 621 /** 622 * Gets a part representing all of the part values with the given name. 623 * 624 * <p> 625 * If more that one part with the given name exists the values will be combined with <js>", "</js> as per 626 * <a href='https://tools.ietf.org/html/rfc2616#section-4.2'>RFC 2616 Section 4.2</a>. 627 * 628 * @param name The part name. 629 * @return A part with a condensed value, or {@link Optional#empty()} if no parts by the given name are present 630 */ 631 public Optional<NameValuePair> get(String name) { 632 633 NameValuePair first = null; 634 List<NameValuePair> rest = null; 635 for (NameValuePair x : this) { 636 if (eq(x.getName(), name)) { 637 if (first == null) 638 first = x; 639 else { 640 if (rest == null) 641 rest = list(); 642 rest.add(x); 643 } 644 } 645 } 646 647 if (first == null) 648 return empty(); 649 650 if (rest == null) 651 return optional(first); 652 653 CharArrayBuffer sb = new CharArrayBuffer(128); 654 sb.append(first.getValue()); 655 for (int i = 0; i < rest.size(); i++) { 656 sb.append(','); 657 sb.append(rest.get(i).getValue()); 658 } 659 660 return optional(new BasicStringPart(name, sb.toString())); 661 } 662 663 /** 664 * Gets a part representing all of the part values with the given name. 665 * 666 * <p> 667 * If more that one part with the given name exists the values will be combined with <js>", "</js> as per 668 * <a href='https://tools.ietf.org/html/rfc2616#section-4.2'>RFC 2616 Section 4.2</a>. 669 * 670 * <p> 671 * The implementation class must have a public constructor taking in one of the following argument lists: 672 * <ul> 673 * <li><c>X(String <jv>value</jv>)</c> 674 * <li><c>X(Object <jv>value</jv>)</c> 675 * <li><c>X(String <jv>name</jv>, String <jv>value</jv>)</c> 676 * <li><c>X(String <jv>name</jv>, Object <jv>value</jv>)</c> 677 * </ul> 678 * 679 * <h5 class='figure'>Example</h5> 680 * <p class='bjava'> 681 * BasicIntegerPart <jv>age</jv> = <jv>partList</jv>.get(<js>"age"</js>, BasicIntegerPart.<jk>class</jk>); 682 * </p> 683 * 684 * @param <T> The part implementation class. 685 * @param name The part name. 686 * @param type The part implementation class. 687 * @return A part with a condensed value or <jk>null</jk> if no parts by the given name are present 688 */ 689 public <T> Optional<T> get(String name, Class<T> type) { 690 691 NameValuePair first = null; 692 List<NameValuePair> rest = null; 693 for (NameValuePair x : this) { 694 if (eq(x.getName(), name)) { 695 if (first == null) 696 first = x; 697 else { 698 if (rest == null) 699 rest = list(); 700 rest.add(x); 701 } 702 } 703 } 704 705 if (first == null) 706 return empty(); 707 708 if (rest == null) 709 return optional(PartBeanMeta.of(type).construct(name, first.getValue())); 710 711 CharArrayBuffer sb = new CharArrayBuffer(128); 712 sb.append(first.getValue()); 713 for (int i = 0; i < rest.size(); i++) { 714 sb.append(','); 715 sb.append(rest.get(i).getValue()); 716 } 717 718 return optional(PartBeanMeta.of(type).construct(name, sb.toString())); 719 } 720 721 /** 722 * Gets a part representing all of the part values with the given name. 723 * 724 * <p> 725 * 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. 726 * 727 * <h5 class='figure'>Example</h5> 728 * <p class='bjava'> 729 * Age <jv>age</jv> = <jv>partList</jv>.get(Age.<jk>class</jk>); 730 * </p> 731 * 732 * @param <T> The part implementation class. 733 * @param type The part implementation class. 734 * @return A part with a condensed value or <jk>null</jk> if no parts by the given name are present 735 */ 736 public <T> Optional<T> get(Class<T> type) { 737 assertArgNotNull("type", type); 738 739 String name = PartBeanMeta.of(type).getSchema().getName(); 740 assertArg(name != null, "Part name could not be found on bean type ''{0}''", type.getName()); 741 742 return get(name, type); 743 } 744 745 /** 746 * Gets all of the parts. 747 * 748 * <p> 749 * The returned array maintains the relative order in which the parts were added. 750 * Each call creates a new array not backed by this list. 751 * 752 * <p> 753 * As a general rule, it's more efficient to use the other methods with consumers to 754 * get parts. 755 * 756 * @return An array containing all parts, never <jk>null</jk>. 757 */ 758 public NameValuePair[] getAll() { 759 return stream().toArray(NameValuePair[]::new); 760 } 761 762 /** 763 * Gets all of the parts with the given name. 764 * 765 * <p> 766 * The returned array maintains the relative order in which the parts were added. 767 * Part name comparison is case sensitive by default. 768 * Parts with null values are ignored. 769 * Each call creates a new array not backed by this list. 770 * 771 * <p> 772 * As a general rule, it's more efficient to use the other methods with consumers to 773 * get parts. 774 * 775 * @param name The part name. 776 * 777 * @return An array containing all matching parts, never <jk>null</jk>. 778 */ 779 public NameValuePair[] getAll(String name) { 780 return stream().filter(x -> eq(x.getName(), name)).toArray(NameValuePair[]::new); 781 } 782 783 /** 784 * Performs an action on the values for all matching parts in this list. 785 * 786 * @param filter A predicate to apply to each element to determine if it should be included. Can be <jk>null</jk>. 787 * @param action An action to perform on each element. 788 * @return This object. 789 */ 790 public PartList forEachValue(Predicate<NameValuePair> filter, Consumer<String> action) { 791 return forEach(filter, x -> action.accept(x.getValue())); 792 } 793 794 /** 795 * Performs an action on the values of all matching parts in this list. 796 * 797 * @param name The part name. 798 * @param action An action to perform on each element. 799 * @return This object. 800 */ 801 public PartList forEachValue(String name, Consumer<String> action) { 802 return forEach(name, x -> action.accept(x.getValue())); 803 } 804 805 /** 806 * Returns all the string values for all parts with the specified name. 807 * 808 * @param name The part name. 809 * @return An array containing all values. Never <jk>null</jk>. 810 */ 811 public String[] getValues(String name) { 812 return stream().filter(x -> eq(x.getName(), name)).map(x -> x.getValue()).toArray(String[]::new); 813 } 814 815 /** 816 * Tests if parts with the given name are contained within this list. 817 * 818 * <p> 819 * Part name comparison is case insensitive. 820 * 821 * @param name The part name. 822 * @return <jk>true</jk> if at least one part with the name is present. 823 */ 824 public boolean contains(String name) { 825 return stream().anyMatch(x -> eq(x.getName(), name)); 826 } 827 828 /** 829 * Returns an iterator over this list of parts. 830 * 831 * @return A new iterator over this list of parts. 832 */ 833 public PartIterator partIterator() { 834 return new BasicPartIterator(toArray(new NameValuePair[0]), null, caseInsensitive); 835 } 836 837 /** 838 * Returns an iterator over the parts with a given name in this list. 839 * 840 * @param name The name of the parts over which to iterate, or <jk>null</jk> for all parts 841 * 842 * @return A new iterator over the matching parts in this list. 843 */ 844 public PartIterator partIterator(String name) { 845 return new BasicPartIterator(getAll(name), name, caseInsensitive); 846 } 847 848 /** 849 * Performs an action on all parts in this list with the specified name. 850 * 851 * <p> 852 * This is the preferred method for iterating over parts as it does not involve 853 * creation or copy of lists/arrays. 854 * 855 * @param name The parts name. 856 * @param action An action to perform on each element. 857 * @return This object. 858 */ 859 public PartList forEach(String name, Consumer<NameValuePair> action) { 860 return forEach(x -> eq(name, x.getName()), action); 861 } 862 863 /** 864 * Performs an action on all the matching parts in this list. 865 * 866 * <p> 867 * This is the preferred method for iterating over parts as it does not involve 868 * creation or copy of lists/arrays. 869 * 870 * @param filter A predicate to apply to each element to determine if it should be included. Can be <jk>null</jk>. 871 * @param action An action to perform on each element. 872 * @return This object. 873 */ 874 public PartList forEach(Predicate<NameValuePair> filter, Consumer<NameValuePair> action) { 875 forEach(x -> consume(filter, action, x)); 876 return this; 877 } 878 879 /** 880 * Returns a stream of the parts in this list with the specified name. 881 * 882 * <p> 883 * This does not involve a copy of the underlying array of <c>NameValuePair</c> objects so should perform well. 884 * 885 * @param name The part name. 886 * @return This object. 887 */ 888 public Stream<NameValuePair> stream(String name) { 889 return stream().filter(x->eq(name, x.getName())); 890 } 891 892 //------------------------------------------------------------------------------------------------------------- 893 // Other methods 894 //------------------------------------------------------------------------------------------------------------- 895 896 private NameValuePair createPart(String name, Object value) { 897 boolean isResolving = varResolver != null; 898 899 if (value instanceof Supplier<?>) { 900 Supplier<?> value2 = (Supplier<?>)value; 901 return isResolving ? new BasicPart(name, resolver(value2)) : new BasicPart(name, value2); 902 } 903 return isResolving ? new BasicPart(name, resolver(value)) : new BasicPart(name, value); 904 } 905 906 private Supplier<Object> resolver(Object input) { 907 return ()->varResolver.resolve(stringify(unwrap(input))); 908 } 909 910 private Object unwrap(Object o) { 911 while (o instanceof Supplier) 912 o = ((Supplier<?>)o).get(); 913 return o; 914 } 915 916 private boolean eq(String s1, String s2) { 917 return caseInsensitive ? StringUtils.eqic(s1, s2) : StringUtils.eq(s1, s2); 918 } 919 920 /** 921 * Returns this list as a URL-encoded custom query. 922 */ 923 @Override /* Object */ 924 public String toString() { 925 StringBuilder sb = new StringBuilder(); 926 forEach(p -> { 927 if (p != null) { 928 String v = p.getValue(); 929 if (v != null) { 930 if (sb.length() > 0) 931 sb.append("&"); 932 sb.append(urlEncode(p.getName())).append('=').append(urlEncode(p.getValue())); 933 } 934 } 935 }); 936 return sb.toString(); 937 } 938 939 // <FluentSetters> 940 941 @Override /* GENERATED - org.apache.juneau.collections.ControlledArrayList */ 942 public PartList setUnmodifiable() { 943 super.setUnmodifiable(); 944 return this; 945 } 946 947 // </FluentSetters> 948}