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