001/* 002 * Copyright 2015-2022 Transmogrify LLC, 2022-2026 Revetware LLC. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.pyranid; 018 019import com.pyranid.JsonParameter.BindingPreference; 020import org.jspecify.annotations.NonNull; 021import org.jspecify.annotations.Nullable; 022 023import javax.annotation.concurrent.ThreadSafe; 024import java.lang.reflect.Array; 025import java.lang.reflect.Type; 026import java.math.BigDecimal; 027import java.util.Collection; 028import java.util.List; 029import java.util.Map; 030import java.util.Optional; 031import java.util.Set; 032 033import static java.util.Objects.requireNonNull; 034 035/** 036 * Fluent interface for acquiring instances of specialized parameter types. 037 * 038 * @author <a href="https://www.revetkn.com">Mark Allen</a> 039 * @since 3.0.0 040 */ 041@ThreadSafe 042public final class Parameters { 043 private Parameters() { 044 // Prevents instantiation 045 } 046 047 /** 048 * Acquires a SQL ARRAY parameter for a {@link List} given an appropriate <a href="https://docs.oracle.com/en/java/javase/24/docs/api/java.sql/java/sql/Array.html#getBaseTypeName()" target="_blank">{@code java.sql.Array#getBaseTypeName()}</a>. 049 * <p> 050 * You may determine available {@code baseTypeName} values for your database by examining metadata exposed via {@link Database#readDatabaseMetaData(DatabaseMetaDataReader)}. 051 * <p> 052 * For non-SQL array binding where you need to preserve the Java element type for custom binders, 053 * use {@link #arrayOf(Class, Object)} instead. 054 * 055 * @param baseTypeName the SQL ARRAY element type, e.g. {@code "text"}, {@code "uuid"}, {@code "float4"}, {@code "float8"} ... 056 * @param list the list whose elements will be used to populate the SQL ARRAY 057 * @param <E> the element type of the Java list ({@code List<E>}); each element must be bindable to {@code baseTypeName} by the active {@link PreparedStatementBinder}. 058 * @return a SQL ARRAY parameter for the given list 059 */ 060 @NonNull 061 public static <E> SqlArrayParameter<E> sqlArrayOf(@NonNull String baseTypeName, 062 @Nullable List<E> list) { 063 requireNonNull(baseTypeName); 064 return new DefaultSqlArrayParameter(baseTypeName, list == null ? null : list.toArray()); 065 } 066 067 /** 068 * Acquires a SQL ARRAY parameter for a native Java array given an appropriate <a href="https://docs.oracle.com/en/java/javase/24/docs/api/java.sql/java/sql/Array.html#getBaseTypeName()" target="_blank">{@code java.sql.Array#getBaseTypeName()}</a>. 069 * <p> 070 * You may determine available {@code baseTypeName} values for your database by examining metadata exposed via {@link Database#readDatabaseMetaData(DatabaseMetaDataReader)}. 071 * <p> 072 * For non-SQL array binding where you need to preserve the Java element type for custom binders, 073 * use {@link #arrayOf(Class, Object)} instead. 074 * 075 * @param baseTypeName the SQL ARRAY element type, e.g. {@code "text"}, {@code "uuid"}, {@code "float4"}, {@code "float8"} ... 076 * @param array the native Java array whose elements will be used to populate the SQL ARRAY 077 * @param <E> the element type of the Java array ({@code T[]}); each element must be bindable to {@code baseTypeName} by the active {@link PreparedStatementBinder}. 078 * @return a SQL ARRAY parameter for the given Java array 079 */ 080 @NonNull 081 public static <E> SqlArrayParameter<E> sqlArrayOf(@NonNull String baseTypeName, 082 E @Nullable [] array) { 083 requireNonNull(baseTypeName); 084 return new DefaultSqlArrayParameter(baseTypeName, array); 085 } 086 087 /** 088 * Default package-private implementation of {@link SqlArrayParameter}. 089 * 090 * @author <a href="https://www.revetkn.com">Mark Allen</a> 091 * @since 3.0.0 092 */ 093 @ThreadSafe 094 static class DefaultSqlArrayParameter implements SqlArrayParameter { 095 @NonNull 096 private final String baseTypeName; // e.g. "text", "uuid", "integer", ... 097 @Nullable 098 private final Object @Nullable [] elements; 099 100 DefaultSqlArrayParameter(@NonNull String baseTypeName, 101 Object @Nullable [] elements) { 102 requireNonNull(baseTypeName); 103 104 this.baseTypeName = baseTypeName; 105 this.elements = elements == null ? null : elements.clone(); // Always perform a defensive copy 106 } 107 108 /** 109 * Gets the element type of this SQL ARRAY, which corresponds to the value of <a href="https://docs.oracle.com/en/java/javase/24/docs/api/java.sql/java/sql/Array.html#getBaseTypeName()" target="_blank">{@code java.sql.Array#getBaseTypeName()}</a> 110 * and is database-specific. 111 * 112 * @return the element type of this SQL ARRAY 113 */ 114 @NonNull 115 @Override 116 public String getBaseTypeName() { 117 return this.baseTypeName; 118 } 119 120 /** 121 * Gets the elements of this SQL ARRAY. 122 * 123 * @return the elements of this SQL ARRAY 124 */ 125 @NonNull 126 @Override 127 public Optional<Object[]> getElements() { 128 // Defensive copy 129 return this.elements == null ? Optional.empty() : Optional.of(this.elements.clone()); 130 } 131 } 132 133 /** 134 * Acquires a parameter for SQL {@code IN} list expansion using a {@link Collection}. 135 * Elements must be non-empty. 136 * 137 * @param elements the elements to expand into SQL {@code IN} list placeholders 138 * @param <E> the element type 139 * @return an IN-list parameter for the given elements 140 */ 141 @NonNull 142 public static <E> InListParameter inList(@NonNull Collection<@Nullable E> elements) { 143 requireNonNull(elements); 144 return new DefaultInListParameter(elements.toArray()); 145 } 146 147 /** 148 * Acquires a parameter for SQL {@code IN} list expansion using a Java array. 149 * Elements must be non-empty. 150 * 151 * @param elements the elements to expand into SQL {@code IN} list placeholders 152 * @param <E> the element type 153 * @return an IN-list parameter for the given elements 154 */ 155 @NonNull 156 public static <E> InListParameter inList(@NonNull E @Nullable [] elements) { 157 requireNonNull(elements); 158 return new DefaultInListParameter(elements); 159 } 160 161 /** 162 * Acquires a parameter for SQL {@code IN} list expansion using a {@code byte[]} array. 163 * Elements must be non-empty. 164 * 165 * @param elements the elements to expand into SQL {@code IN} list placeholders 166 * @return an IN-list parameter for the given elements 167 */ 168 @NonNull 169 public static InListParameter inList(byte @NonNull [] elements) { 170 requireNonNull(elements); 171 Object[] boxed = new Object[elements.length]; 172 for (int i = 0; i < elements.length; i++) 173 boxed[i] = elements[i]; 174 return new DefaultInListParameter(boxed); 175 } 176 177 /** 178 * Acquires a parameter for SQL {@code IN} list expansion using a {@code short[]} array. 179 * Elements must be non-empty. 180 * 181 * @param elements the elements to expand into SQL {@code IN} list placeholders 182 * @return an IN-list parameter for the given elements 183 */ 184 @NonNull 185 public static InListParameter inList(short @NonNull [] elements) { 186 requireNonNull(elements); 187 Object[] boxed = new Object[elements.length]; 188 for (int i = 0; i < elements.length; i++) 189 boxed[i] = elements[i]; 190 return new DefaultInListParameter(boxed); 191 } 192 193 /** 194 * Acquires a parameter for SQL {@code IN} list expansion using an {@code int[]} array. 195 * Elements must be non-empty. 196 * 197 * @param elements the elements to expand into SQL {@code IN} list placeholders 198 * @return an IN-list parameter for the given elements 199 */ 200 @NonNull 201 public static InListParameter inList(int @NonNull [] elements) { 202 requireNonNull(elements); 203 Object[] boxed = new Object[elements.length]; 204 for (int i = 0; i < elements.length; i++) 205 boxed[i] = elements[i]; 206 return new DefaultInListParameter(boxed); 207 } 208 209 /** 210 * Acquires a parameter for SQL {@code IN} list expansion using a {@code long[]} array. 211 * Elements must be non-empty. 212 * 213 * @param elements the elements to expand into SQL {@code IN} list placeholders 214 * @return an IN-list parameter for the given elements 215 */ 216 @NonNull 217 public static InListParameter inList(long @NonNull [] elements) { 218 requireNonNull(elements); 219 Object[] boxed = new Object[elements.length]; 220 for (int i = 0; i < elements.length; i++) 221 boxed[i] = elements[i]; 222 return new DefaultInListParameter(boxed); 223 } 224 225 /** 226 * Acquires a parameter for SQL {@code IN} list expansion using a {@code float[]} array. 227 * Elements must be non-empty. 228 * 229 * @param elements the elements to expand into SQL {@code IN} list placeholders 230 * @return an IN-list parameter for the given elements 231 */ 232 @NonNull 233 public static InListParameter inList(float @NonNull [] elements) { 234 requireNonNull(elements); 235 Object[] boxed = new Object[elements.length]; 236 for (int i = 0; i < elements.length; i++) 237 boxed[i] = elements[i]; 238 return new DefaultInListParameter(boxed); 239 } 240 241 /** 242 * Acquires a parameter for SQL {@code IN} list expansion using a {@code double[]} array. 243 * Elements must be non-empty. 244 * 245 * @param elements the elements to expand into SQL {@code IN} list placeholders 246 * @return an IN-list parameter for the given elements 247 */ 248 @NonNull 249 public static InListParameter inList(double @NonNull [] elements) { 250 requireNonNull(elements); 251 Object[] boxed = new Object[elements.length]; 252 for (int i = 0; i < elements.length; i++) 253 boxed[i] = elements[i]; 254 return new DefaultInListParameter(boxed); 255 } 256 257 /** 258 * Acquires a parameter for SQL {@code IN} list expansion using a {@code boolean[]} array. 259 * Elements must be non-empty. 260 * 261 * @param elements the elements to expand into SQL {@code IN} list placeholders 262 * @return an IN-list parameter for the given elements 263 */ 264 @NonNull 265 public static InListParameter inList(boolean @NonNull [] elements) { 266 requireNonNull(elements); 267 Object[] boxed = new Object[elements.length]; 268 for (int i = 0; i < elements.length; i++) 269 boxed[i] = elements[i]; 270 return new DefaultInListParameter(boxed); 271 } 272 273 /** 274 * Acquires a parameter for SQL {@code IN} list expansion using a {@code char[]} array. 275 * Elements must be non-empty. 276 * 277 * @param elements the elements to expand into SQL {@code IN} list placeholders 278 * @return an IN-list parameter for the given elements 279 */ 280 @NonNull 281 public static InListParameter inList(char @NonNull [] elements) { 282 requireNonNull(elements); 283 Object[] boxed = new Object[elements.length]; 284 for (int i = 0; i < elements.length; i++) 285 boxed[i] = elements[i]; 286 return new DefaultInListParameter(boxed); 287 } 288 289 /** 290 * Default package-private implementation of {@link InListParameter}. 291 * 292 * @author <a href="https://www.revetkn.com">Mark Allen</a> 293 * @since 4.0.0 294 */ 295 @ThreadSafe 296 static class DefaultInListParameter implements InListParameter { 297 @NonNull 298 private final Object[] elements; 299 300 DefaultInListParameter(Object @NonNull [] elements) { 301 requireNonNull(elements); 302 this.elements = elements.clone(); // Always perform a defensive copy 303 } 304 305 @NonNull 306 @Override 307 public Object @NonNull [] getElements() { 308 // Defensive copy 309 return this.elements.clone(); 310 } 311 } 312 313 /** 314 * Acquires a parameter of array type, preserving element type information so it is accessible at runtime. 315 * <p> 316 * This is useful when you want to bind an array via a {@link CustomParameterBinder} and need the 317 * component type to be preserved for {@link TargetType} matching. 318 * <p> 319 * For SQL {@code ARRAY} binding, use {@link #sqlArrayOf(String, Object[])} instead. 320 * If you need a formal SQL {@code ARRAY} parameter for JDBC array binding, do not use this method. 321 * <p> 322 * <strong>Note:</strong> this kind of parameter requires a corresponding {@link CustomParameterBinder} 323 * to be registered, even when the wrapped value is {@code null}; implement {@link CustomParameterBinder#bindNull} 324 * if you want typed nulls to bind successfully. 325 * 326 * @param elementType the element type of the array 327 * @param array the array value to wrap; may be {@code null} 328 * @param <E> the element type of the array 329 * @return a {@link TypedParameter} representing a {@code E[]} suitable for use with custom binders 330 */ 331 @NonNull 332 public static <E> TypedParameter arrayOf(@NonNull Class<E> elementType, 333 E @Nullable [] array) { 334 requireNonNull(elementType); 335 return arrayOf((Class<?>) elementType, array); 336 } 337 338 /** 339 * Acquires a parameter of array type, preserving element type information so it is accessible at runtime. 340 * <p> 341 * This overload supports primitive arrays by passing the primitive component class 342 * (for example, {@code int.class}) and the corresponding primitive array. 343 * <p> 344 * For SQL {@code ARRAY} binding, use {@link #sqlArrayOf(String, Object[])} instead. 345 * If you need a formal SQL {@code ARRAY} parameter for JDBC array binding, do not use this method. 346 * <p> 347 * <strong>Note:</strong> this kind of parameter requires a corresponding {@link CustomParameterBinder} 348 * to be registered, even when the wrapped value is {@code null}; implement {@link CustomParameterBinder#bindNull} 349 * if you want typed nulls to bind successfully. 350 * 351 * @param elementType the element type of the array (may be primitive) 352 * @param array the array value to wrap; may be {@code null} 353 * @return a {@link TypedParameter} representing an array suitable for use with custom binders 354 */ 355 @NonNull 356 public static TypedParameter arrayOf(@NonNull Class<?> elementType, 357 @Nullable Object array) { 358 requireNonNull(elementType); 359 360 if (array != null) { 361 Class<?> arrayClass = array.getClass(); 362 363 if (!arrayClass.isArray()) 364 throw new IllegalArgumentException("Array parameter is not an array"); 365 366 Class<?> componentType = arrayClass.getComponentType(); 367 368 if (elementType.isPrimitive()) { 369 if (!componentType.equals(elementType)) 370 throw new IllegalArgumentException("Array parameter component type does not match elementType"); 371 } else if (!elementType.isAssignableFrom(componentType)) { 372 throw new IllegalArgumentException("Array parameter component type is not assignable to elementType"); 373 } 374 } 375 376 Class<?> arrayType = Array.newInstance(elementType, 0).getClass(); 377 return new DefaultTypedParameter(arrayType, array); 378 } 379 380 /** 381 * Acquires a vector parameter for an array of {@code double}. 382 * 383 * @param elements the elements of the vector parameter 384 * @return the vector parameter 385 */ 386 @NonNull 387 public static VectorParameter vectorOfDoubles(double @Nullable [] elements) { 388 return new DefaultVectorParameter(elements); 389 } 390 391 /** 392 * Acquires a vector parameter for a {@link List} of {@link Double}. 393 * 394 * @param elements the elements of the vector parameter 395 * @return the vector parameter 396 */ 397 @NonNull 398 public static VectorParameter vectorOfDoubles(@Nullable List<@NonNull Double> elements) { 399 if (elements == null) 400 return new DefaultVectorParameter(null); 401 402 double[] doubles = new double[elements.size()]; 403 for (int i = 0; i < doubles.length; i++) doubles[i] = requireNonNull(elements.get(i)); 404 return new DefaultVectorParameter(doubles); 405 } 406 407 /** 408 * Acquires a vector parameter for an array of {@code float}. 409 * 410 * @param elements the elements of the vector parameter 411 * @return the vector parameter 412 */ 413 @NonNull 414 public static VectorParameter vectorOfFloats(float @Nullable [] elements) { 415 if (elements == null) 416 return new DefaultVectorParameter(null); 417 418 double[] doubles = new double[elements.length]; 419 for (int i = 0; i < elements.length; i++) doubles[i] = elements[i]; 420 return new DefaultVectorParameter(doubles); 421 } 422 423 /** 424 * Acquires a vector parameter for a {@link List} of {@link Float}. 425 * 426 * @param elements the elements of the vector parameter 427 * @return the vector parameter 428 */ 429 @NonNull 430 public static VectorParameter vectorOfFloats(@Nullable List<@NonNull Float> elements) { 431 if (elements == null) 432 return new DefaultVectorParameter(null); 433 434 double[] doubles = new double[elements.size()]; 435 for (int i = 0; i < doubles.length; i++) doubles[i] = requireNonNull(elements.get(i)); 436 return new DefaultVectorParameter(doubles); 437 } 438 439 /** 440 * Acquires a vector parameter for a {@link List} of {@link BigDecimal}. 441 * 442 * @param elements the elements of the vector parameter 443 * @return the vector parameter 444 */ 445 @NonNull 446 public static VectorParameter vectorOfBigDecimals(@Nullable List<@NonNull BigDecimal> elements) { 447 if (elements == null) 448 return new DefaultVectorParameter(null); 449 450 double[] d = new double[elements.size()]; 451 for (int i = 0; i < d.length; i++) d[i] = requireNonNull(elements.get(i)).doubleValue(); 452 return new DefaultVectorParameter(d); 453 } 454 455 /** 456 * Default package-private implementation of {@link VectorParameter}. 457 * 458 * @author <a href="https://www.revetkn.com">Mark Allen</a> 459 * @since 3.0.0 460 */ 461 @ThreadSafe 462 static class DefaultVectorParameter implements VectorParameter { 463 @Nullable 464 private final double[] elements; 465 466 private DefaultVectorParameter(double @Nullable [] elements) { 467 if (elements == null) { 468 this.elements = null; 469 return; 470 } 471 472 if (elements.length == 0) 473 throw new IllegalArgumentException("Vector parameters must have at least 1 element"); 474 475 for (double d : elements) 476 if (!Double.isFinite(d)) 477 throw new IllegalArgumentException("Vector parameter elements must be finite (no NaN/Infinity)"); 478 479 // Always defensive copy 480 this.elements = elements.clone(); 481 } 482 483 /** 484 * Gets the elements of this vector. 485 * 486 * @return the elements of this vector 487 */ 488 @NonNull 489 @Override 490 public Optional<double[]> getElements() { 491 // Defensive copy 492 return this.elements == null ? Optional.empty() : Optional.of(this.elements.clone()); 493 } 494 } 495 496 /** 497 * Acquires a JSON parameter for "stringified" JSON, using {@link BindingPreference#BINARY}. 498 * 499 * @param json the stringified JSON for this parameter 500 * @return the JSON parameter 501 */ 502 @NonNull 503 public static JsonParameter json(@Nullable String json) { 504 return new DefaultJsonParameter(json, BindingPreference.BINARY); 505 } 506 507 /** 508 * Acquires a JSON parameter for "stringified" JSON. 509 * 510 * @param json the stringified JSON for this parameter 511 * @param bindingPreference how the JSON parameter should be bound to a {@link java.sql.PreparedStatement} 512 * @return the JSON parameter 513 */ 514 @NonNull 515 public static JsonParameter json(@Nullable String json, 516 @NonNull BindingPreference bindingPreference) { 517 requireNonNull(bindingPreference); 518 519 return new DefaultJsonParameter(json, bindingPreference); 520 } 521 522 /** 523 * Default package-private implementation of {@link JsonParameter}. 524 * 525 * @author <a href="https://www.revetkn.com">Mark Allen</a> 526 * @since 3.0.0 527 */ 528 @ThreadSafe 529 static class DefaultJsonParameter implements JsonParameter { 530 @Nullable 531 private final String json; 532 @NonNull 533 private final BindingPreference bindingPreference; 534 535 private DefaultJsonParameter(@Nullable String json, 536 @NonNull BindingPreference bindingPreference) { 537 requireNonNull(bindingPreference); 538 539 this.json = json; 540 this.bindingPreference = bindingPreference; 541 } 542 543 @NonNull 544 @Override 545 public Optional<String> getJson() { 546 return Optional.ofNullable(this.json); 547 } 548 549 @NonNull 550 @Override 551 public BindingPreference getBindingPreference() { 552 return this.bindingPreference; 553 } 554 } 555 556 /** 557 * Acquires a parameter of type {@link List}, preserving type information so it is accessible at runtime. 558 * <p> 559 * This is useful when you want to bind a parameterized collection such as {@code List<UUID>} or 560 * {@code List<String>} and need the generic type argument (e.g. {@code UUID.class}) to be preserved. 561 * <p> 562 * The resulting {@link TypedParameter} carries both the runtime value and its generic type so that 563 * {@link CustomParameterBinder#appliesTo(TargetType)} can match against the element type. 564 * <p> 565 * <strong>Note:</strong> this kind of parameter requires a corresponding {@link CustomParameterBinder} 566 * to be registered, even when the wrapped value is {@code null}; implement {@link CustomParameterBinder#bindNull} 567 * if you want typed nulls to bind successfully. 568 * 569 * @param elementType the {@link Class} representing the type of elements contained in the list; 570 * used to preserve generic type information 571 * @param list the list value to wrap; may be {@code null} 572 * @param <E> the element type of the list 573 * @return a {@link TypedParameter} representing a {@code List<E>} suitable for use with custom binders 574 */ 575 @NonNull 576 public static <E> TypedParameter listOf(@NonNull Class<E> elementType, 577 @Nullable List<E> list) { 578 requireNonNull(elementType); 579 580 Type listOfE = new DefaultParameterizedType(List.class, new Type[]{elementType}, null); 581 return new DefaultTypedParameter(listOfE, list); 582 } 583 584 /** 585 * Acquires a parameter of type {@link Set}, preserving type information so it is accessible at runtime. 586 * <p> 587 * This is useful when you want to bind a parameterized collection such as {@code Set<UUID>} or 588 * {@code Set<String>} and need the generic type argument (e.g. {@code UUID.class}) to be preserved. 589 * <p> 590 * The resulting {@link TypedParameter} carries both the runtime value and its generic type so that 591 * {@link CustomParameterBinder#appliesTo(TargetType)} can match against the element type. 592 * <p> 593 * <strong>Note:</strong> this kind of parameter requires a corresponding {@link CustomParameterBinder} 594 * to be registered, even when the wrapped value is {@code null}; implement {@link CustomParameterBinder#bindNull} 595 * if you want typed nulls to bind successfully. 596 * 597 * @param elementType the {@link Class} representing the type of elements contained in the set; 598 * used to preserve generic type information 599 * @param set the set value to wrap; may be {@code null} 600 * @param <E> the element type of the set 601 * @return a {@link TypedParameter} representing a {@code Set<E>} suitable for use with custom binders 602 */ 603 @NonNull 604 public static <E> TypedParameter setOf(@NonNull Class<E> elementType, 605 @Nullable Set<E> set) { 606 requireNonNull(elementType); 607 608 Type setOfE = new DefaultParameterizedType(Set.class, new Type[]{elementType}, null); 609 return new DefaultTypedParameter(setOfE, set); 610 } 611 612 /** 613 * Acquires a parameter of type {@link Map}, preserving key and value type information 614 * so they are accessible at runtime. 615 * <p> 616 * This is useful when you want to bind a parameterized collection such as {@code Map<UUID, Integer>} 617 * and need the generic type arguments (e.g. {@code UUID.class} and {@code Integer.class}) to be preserved. 618 * <p> 619 * The resulting {@link TypedParameter} carries both the runtime value and its generic type so that 620 * {@link CustomParameterBinder#appliesTo(TargetType)} can match against the element type. 621 * <p> 622 * <strong>Note:</strong> this kind of parameter requires a corresponding {@link CustomParameterBinder} 623 * to be registered, even when the wrapped value is {@code null}; implement {@link CustomParameterBinder#bindNull} 624 * if you want typed nulls to bind successfully. 625 * 626 * @param keyType the type of the map keys 627 * @param valueType the type of the map values 628 * @param map the map value; may be {@code null} 629 * @param <K> the key type 630 * @param <V> the value type 631 * @return a {@link TypedParameter} representing {@code Map<K,V>} 632 */ 633 @NonNull 634 public static <K, V> TypedParameter mapOf(@NonNull Class<K> keyType, 635 @NonNull Class<V> valueType, 636 @Nullable Map<K, V> map) { 637 requireNonNull(keyType); 638 requireNonNull(valueType); 639 640 Type mapOfKV = new DefaultParameterizedType(Map.class, new Type[]{keyType, valueType}, null); 641 return new DefaultTypedParameter(mapOfKV, map); 642 } 643}