|
10 | 10 | import java.lang.reflect.Field;
|
11 | 11 | import java.math.BigDecimal;
|
12 | 12 | import java.math.BigInteger;
|
| 13 | +import java.net.InetAddress; |
| 14 | +import java.net.URL; |
| 15 | +import java.sql.Blob; |
| 16 | +import java.sql.Clob; |
| 17 | +import java.sql.NClob; |
| 18 | +import java.sql.Timestamp; |
| 19 | +import java.time.Duration; |
| 20 | +import java.time.Instant; |
| 21 | +import java.time.LocalDate; |
| 22 | +import java.time.LocalDateTime; |
| 23 | +import java.time.LocalTime; |
| 24 | +import java.time.OffsetDateTime; |
| 25 | +import java.time.OffsetTime; |
| 26 | +import java.time.Year; |
| 27 | +import java.time.ZoneId; |
| 28 | +import java.time.ZoneOffset; |
| 29 | +import java.time.ZonedDateTime; |
13 | 30 | import java.util.ArrayList;
|
| 31 | +import java.util.Calendar; |
| 32 | +import java.util.Currency; |
| 33 | +import java.util.GregorianCalendar; |
14 | 34 | import java.util.List;
|
| 35 | +import java.util.Locale; |
| 36 | +import java.util.TimeZone; |
15 | 37 | import java.util.UUID;
|
16 | 38 | import java.util.function.Consumer;
|
17 | 39 |
|
|
136 | 158 | import org.hibernate.models.spi.MutableMemberDetails;
|
137 | 159 | import org.hibernate.models.spi.SourceModelBuildingContext;
|
138 | 160 | import org.hibernate.type.SqlTypes;
|
| 161 | +import org.hibernate.type.descriptor.java.BasicJavaType; |
| 162 | +import org.hibernate.type.descriptor.java.BigDecimalJavaType; |
| 163 | +import org.hibernate.type.descriptor.java.BigIntegerJavaType; |
| 164 | +import org.hibernate.type.descriptor.java.BlobJavaType; |
| 165 | +import org.hibernate.type.descriptor.java.BooleanJavaType; |
| 166 | +import org.hibernate.type.descriptor.java.ByteJavaType; |
| 167 | +import org.hibernate.type.descriptor.java.CalendarJavaType; |
| 168 | +import org.hibernate.type.descriptor.java.CharacterJavaType; |
| 169 | +import org.hibernate.type.descriptor.java.ClassJavaType; |
| 170 | +import org.hibernate.type.descriptor.java.ClobJavaType; |
| 171 | +import org.hibernate.type.descriptor.java.CurrencyJavaType; |
| 172 | +import org.hibernate.type.descriptor.java.DateJavaType; |
| 173 | +import org.hibernate.type.descriptor.java.DoubleJavaType; |
| 174 | +import org.hibernate.type.descriptor.java.DurationJavaType; |
| 175 | +import org.hibernate.type.descriptor.java.InetAddressJavaType; |
| 176 | +import org.hibernate.type.descriptor.java.InstantJavaType; |
| 177 | +import org.hibernate.type.descriptor.java.IntegerJavaType; |
| 178 | +import org.hibernate.type.descriptor.java.LocalDateJavaType; |
| 179 | +import org.hibernate.type.descriptor.java.LocalDateTimeJavaType; |
| 180 | +import org.hibernate.type.descriptor.java.LocalTimeJavaType; |
| 181 | +import org.hibernate.type.descriptor.java.LocaleJavaType; |
| 182 | +import org.hibernate.type.descriptor.java.LongJavaType; |
| 183 | +import org.hibernate.type.descriptor.java.NClobJavaType; |
| 184 | +import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType; |
| 185 | +import org.hibernate.type.descriptor.java.OffsetTimeJavaType; |
| 186 | +import org.hibernate.type.descriptor.java.ShortJavaType; |
| 187 | +import org.hibernate.type.descriptor.java.StringJavaType; |
| 188 | +import org.hibernate.type.descriptor.java.TimeZoneJavaType; |
| 189 | +import org.hibernate.type.descriptor.java.UUIDJavaType; |
| 190 | +import org.hibernate.type.descriptor.java.UrlJavaType; |
| 191 | +import org.hibernate.type.descriptor.java.YearJavaType; |
| 192 | +import org.hibernate.type.descriptor.java.ZoneIdJavaType; |
| 193 | +import org.hibernate.type.descriptor.java.ZoneOffsetJavaType; |
| 194 | +import org.hibernate.type.descriptor.java.ZonedDateTimeJavaType; |
| 195 | +import org.hibernate.usertype.UserType; |
139 | 196 |
|
140 | 197 | import jakarta.persistence.AssociationOverride;
|
141 | 198 | import jakarta.persistence.AttributeOverride;
|
|
146 | 203 | import jakarta.persistence.Index;
|
147 | 204 | import jakarta.persistence.PrimaryKeyJoinColumn;
|
148 | 205 | import jakarta.persistence.SecondaryTable;
|
| 206 | +import jakarta.persistence.Temporal; |
149 | 207 | import jakarta.persistence.TemporalType;
|
150 | 208 | import jakarta.persistence.UniqueConstraint;
|
151 | 209 | import org.checkerframework.checker.nullness.qual.Nullable;
|
@@ -244,20 +302,305 @@ public static void applyUserType(
|
244 | 302 | JaxbUserTypeImpl jaxbType,
|
245 | 303 | MutableMemberDetails memberDetails,
|
246 | 304 | XmlDocumentContext xmlDocumentContext) {
|
247 |
| - if ( jaxbType == null ) { |
| 305 | + if ( jaxbType == null || StringHelper.isEmpty( jaxbType.getValue() ) ) { |
| 306 | + return; |
| 307 | + } |
| 308 | + |
| 309 | + final boolean wasSpecialCase = handleSpecialBasicTypeCases( jaxbType, memberDetails, xmlDocumentContext ); |
| 310 | + if ( wasSpecialCase ) { |
248 | 311 | return;
|
249 | 312 | }
|
250 | 313 |
|
| 314 | + final ClassDetails userTypeImpl = resolveJavaType( jaxbType.getValue(), xmlDocumentContext ); |
| 315 | + assert userTypeImpl.isImplementor( UserType.class ); |
251 | 316 | final TypeAnnotation typeAnn = (TypeAnnotation) memberDetails.applyAnnotationUsage(
|
252 | 317 | HibernateAnnotations.TYPE,
|
253 | 318 | xmlDocumentContext.getModelBuildingContext()
|
254 | 319 | );
|
255 |
| - |
256 |
| - final ClassDetails userTypeImpl = resolveJavaType( jaxbType.getValue(), xmlDocumentContext ); |
257 | 320 | typeAnn.value( userTypeImpl.toJavaClass() );
|
258 | 321 | typeAnn.parameters( collectParameters( jaxbType.getParameters(), xmlDocumentContext ) );
|
259 | 322 | }
|
260 | 323 |
|
| 324 | + private static boolean handleSpecialBasicTypeCases( |
| 325 | + JaxbUserTypeImpl jaxbType, |
| 326 | + MutableMemberDetails memberDetails, |
| 327 | + XmlDocumentContext xmlDocumentContext) { |
| 328 | + if ( jaxbType.getValue().equalsIgnoreCase( "char" ) |
| 329 | + || jaxbType.getValue().equalsIgnoreCase( "character" ) |
| 330 | + || Character.class.getName().equalsIgnoreCase( jaxbType.getValue() ) ) { |
| 331 | + applyJavaTypeAnnotation( memberDetails, CharacterJavaType.class, xmlDocumentContext ); |
| 332 | + return true; |
| 333 | + } |
| 334 | + |
| 335 | + if ( jaxbType.getValue().equalsIgnoreCase( "string" ) |
| 336 | + || String.class.getName().equalsIgnoreCase( jaxbType.getValue() ) ) { |
| 337 | + applyJavaTypeAnnotation( memberDetails, StringJavaType.class, xmlDocumentContext ); |
| 338 | + return true; |
| 339 | + } |
| 340 | + |
| 341 | + if ( jaxbType.getValue().equalsIgnoreCase( "byte" ) |
| 342 | + || Byte.class.getName().equals( jaxbType.getValue() ) ) { |
| 343 | + applyJavaTypeAnnotation( memberDetails, ByteJavaType.class, xmlDocumentContext ); |
| 344 | + return true; |
| 345 | + } |
| 346 | + |
| 347 | + if ( jaxbType.getValue().equalsIgnoreCase( "boolean" ) |
| 348 | + || Boolean.class.getName().equals( jaxbType.getValue() ) ) { |
| 349 | + applyJavaTypeAnnotation( memberDetails, BooleanJavaType.class, xmlDocumentContext ); |
| 350 | + return true; |
| 351 | + } |
| 352 | + |
| 353 | + if ( jaxbType.getValue().equalsIgnoreCase( "short" ) |
| 354 | + || Short.class.getName().equals( jaxbType.getValue() ) ) { |
| 355 | + applyJavaTypeAnnotation( memberDetails, ShortJavaType.class, xmlDocumentContext ); |
| 356 | + return true; |
| 357 | + } |
| 358 | + |
| 359 | + if ( jaxbType.getValue().equalsIgnoreCase( "int" ) |
| 360 | + || jaxbType.getValue().equalsIgnoreCase( "integer" ) |
| 361 | + || Integer.class.getName().equals( jaxbType.getValue() ) ) { |
| 362 | + applyJavaTypeAnnotation( memberDetails, IntegerJavaType.class, xmlDocumentContext ); |
| 363 | + return true; |
| 364 | + } |
| 365 | + |
| 366 | + if ( jaxbType.getValue().equalsIgnoreCase( "long" ) |
| 367 | + || Long.class.getName().equals( jaxbType.getValue() ) ) { |
| 368 | + applyJavaTypeAnnotation( memberDetails, LongJavaType.class, xmlDocumentContext ); |
| 369 | + return true; |
| 370 | + } |
| 371 | + |
| 372 | + if ( jaxbType.getValue().equalsIgnoreCase( "double" ) |
| 373 | + || Double.class.getName().equals( jaxbType.getValue() ) ) { |
| 374 | + applyJavaTypeAnnotation( memberDetails, DoubleJavaType.class, xmlDocumentContext ); |
| 375 | + return true; |
| 376 | + } |
| 377 | + |
| 378 | + if ( jaxbType.getValue().equalsIgnoreCase( "float" ) |
| 379 | + || Float.class.getName().equals( jaxbType.getValue() ) ) { |
| 380 | + applyJavaTypeAnnotation( memberDetails, DoubleJavaType.class, xmlDocumentContext ); |
| 381 | + return true; |
| 382 | + } |
| 383 | + |
| 384 | + if ( jaxbType.getValue().equalsIgnoreCase( "biginteger" ) |
| 385 | + || jaxbType.getValue().equalsIgnoreCase( "big_integer" ) |
| 386 | + || BigInteger.class.getName().equals( jaxbType.getValue() ) ) { |
| 387 | + applyJavaTypeAnnotation( memberDetails, BigIntegerJavaType.class, xmlDocumentContext ); |
| 388 | + return true; |
| 389 | + } |
| 390 | + |
| 391 | + if ( jaxbType.getValue().equalsIgnoreCase( "bigdecimal" ) |
| 392 | + || jaxbType.getValue().equalsIgnoreCase( "big_decimal" ) |
| 393 | + || BigDecimal.class.getName().equals( jaxbType.getValue() ) ) { |
| 394 | + applyJavaTypeAnnotation( memberDetails, BigDecimalJavaType.class, xmlDocumentContext ); |
| 395 | + return true; |
| 396 | + } |
| 397 | + |
| 398 | + if ( jaxbType.getValue().equalsIgnoreCase( "uuid" ) |
| 399 | + || UUID.class.getName().equals( jaxbType.getValue() ) ) { |
| 400 | + applyJavaTypeAnnotation( memberDetails, UUIDJavaType.class, xmlDocumentContext ); |
| 401 | + return true; |
| 402 | + } |
| 403 | + |
| 404 | + if ( jaxbType.getValue().equalsIgnoreCase( "url" ) |
| 405 | + || URL.class.getName().equals( jaxbType.getValue() ) ) { |
| 406 | + applyJavaTypeAnnotation( memberDetails, UrlJavaType.class, xmlDocumentContext ); |
| 407 | + return true; |
| 408 | + } |
| 409 | + |
| 410 | + if ( jaxbType.getValue().equalsIgnoreCase( "inet" ) |
| 411 | + || jaxbType.getValue().equalsIgnoreCase( "inetaddress" ) |
| 412 | + || jaxbType.getValue().equalsIgnoreCase( "inet_address" ) |
| 413 | + || InetAddress.class.getName().equals( jaxbType.getValue() ) ) { |
| 414 | + applyJavaTypeAnnotation( memberDetails, InetAddressJavaType.class, xmlDocumentContext ); |
| 415 | + return true; |
| 416 | + } |
| 417 | + |
| 418 | + if ( jaxbType.getValue().equalsIgnoreCase( "currency" ) |
| 419 | + || Currency.class.getName().equals( jaxbType.getValue() ) ) { |
| 420 | + applyJavaTypeAnnotation( memberDetails, CurrencyJavaType.class, xmlDocumentContext ); |
| 421 | + return true; |
| 422 | + } |
| 423 | + |
| 424 | + if ( jaxbType.getValue().equalsIgnoreCase( "locale" ) |
| 425 | + || Locale.class.getName().equals( jaxbType.getValue() ) ) { |
| 426 | + applyJavaTypeAnnotation( memberDetails, LocaleJavaType.class, xmlDocumentContext ); |
| 427 | + return true; |
| 428 | + } |
| 429 | + |
| 430 | + if ( jaxbType.getValue().equalsIgnoreCase( "class" ) |
| 431 | + || Class.class.getName().equals( jaxbType.getValue() ) ) { |
| 432 | + applyJavaTypeAnnotation( memberDetails, ClassJavaType.class, xmlDocumentContext ); |
| 433 | + return true; |
| 434 | + } |
| 435 | + |
| 436 | + if ( jaxbType.getValue().equalsIgnoreCase( "blob" ) |
| 437 | + || Blob.class.getName().equals( jaxbType.getValue() ) ) { |
| 438 | + applyJavaTypeAnnotation( memberDetails, BlobJavaType.class, xmlDocumentContext ); |
| 439 | + return true; |
| 440 | + } |
| 441 | + |
| 442 | + if ( jaxbType.getValue().equalsIgnoreCase( "clob" ) |
| 443 | + || Clob.class.getName().equals( jaxbType.getValue() ) ) { |
| 444 | + applyJavaTypeAnnotation( memberDetails, ClobJavaType.class, xmlDocumentContext ); |
| 445 | + return true; |
| 446 | + } |
| 447 | + |
| 448 | + if ( jaxbType.getValue().equalsIgnoreCase( "nclob" ) |
| 449 | + || NClob.class.getName().equals( jaxbType.getValue() ) ) { |
| 450 | + applyJavaTypeAnnotation( memberDetails, NClobJavaType.class, xmlDocumentContext ); |
| 451 | + return true; |
| 452 | + } |
| 453 | + |
| 454 | + if ( jaxbType.getValue().equalsIgnoreCase( "instant" ) |
| 455 | + || Instant.class.getName().equals( jaxbType.getValue() ) ) { |
| 456 | + applyJavaTypeAnnotation( memberDetails, InstantJavaType.class, xmlDocumentContext ); |
| 457 | + return true; |
| 458 | + } |
| 459 | + |
| 460 | + if ( jaxbType.getValue().equalsIgnoreCase( "duration" ) |
| 461 | + || Duration.class.getName().equals( jaxbType.getValue() ) ) { |
| 462 | + applyJavaTypeAnnotation( memberDetails, DurationJavaType.class, xmlDocumentContext ); |
| 463 | + return true; |
| 464 | + } |
| 465 | + |
| 466 | + if ( jaxbType.getValue().equalsIgnoreCase( "year" ) |
| 467 | + || Year.class.getName().equals( jaxbType.getValue() ) ) { |
| 468 | + applyJavaTypeAnnotation( memberDetails, YearJavaType.class, xmlDocumentContext ); |
| 469 | + return true; |
| 470 | + } |
| 471 | + |
| 472 | + if ( jaxbType.getValue().equalsIgnoreCase( "localdatetime" ) |
| 473 | + || jaxbType.getValue().equalsIgnoreCase( "local_date_time" ) |
| 474 | + || LocalDateTime.class.getName().equals( jaxbType.getValue() ) ) { |
| 475 | + applyJavaTypeAnnotation( memberDetails, LocalDateTimeJavaType.class, xmlDocumentContext ); |
| 476 | + return true; |
| 477 | + } |
| 478 | + |
| 479 | + if ( jaxbType.getValue().equalsIgnoreCase( "localdate" ) |
| 480 | + || jaxbType.getValue().equalsIgnoreCase( "local_date" ) |
| 481 | + || LocalDate.class.getName().equals( jaxbType.getValue() ) ) { |
| 482 | + applyJavaTypeAnnotation( memberDetails, LocalDateJavaType.class, xmlDocumentContext ); |
| 483 | + return true; |
| 484 | + } |
| 485 | + |
| 486 | + if ( jaxbType.getValue().equalsIgnoreCase( "localtime" ) |
| 487 | + || jaxbType.getValue().equalsIgnoreCase( "local_time" ) |
| 488 | + || LocalTime.class.getName().equals( jaxbType.getValue() ) ) { |
| 489 | + applyJavaTypeAnnotation( memberDetails, LocalTimeJavaType.class, xmlDocumentContext ); |
| 490 | + return true; |
| 491 | + } |
| 492 | + |
| 493 | + if ( jaxbType.getValue().equalsIgnoreCase( "zoneddatetime" ) |
| 494 | + || jaxbType.getValue().equalsIgnoreCase( "zoned_date_time" ) |
| 495 | + || ZonedDateTime.class.getName().equals( jaxbType.getValue() ) ) { |
| 496 | + applyJavaTypeAnnotation( memberDetails, ZonedDateTimeJavaType.class, xmlDocumentContext ); |
| 497 | + return true; |
| 498 | + } |
| 499 | + |
| 500 | + if ( jaxbType.getValue().equalsIgnoreCase( "offsetdatetime" ) |
| 501 | + || jaxbType.getValue().equalsIgnoreCase( "offset_date_time" ) |
| 502 | + || OffsetDateTime.class.getName().equals( jaxbType.getValue() ) ) { |
| 503 | + applyJavaTypeAnnotation( memberDetails, OffsetDateTimeJavaType.class, xmlDocumentContext ); |
| 504 | + return true; |
| 505 | + } |
| 506 | + |
| 507 | + if ( jaxbType.getValue().equalsIgnoreCase( "offsettime" ) |
| 508 | + || jaxbType.getValue().equalsIgnoreCase( "offset_time" ) |
| 509 | + || OffsetTime.class.getName().equals( jaxbType.getValue() ) ) { |
| 510 | + applyJavaTypeAnnotation( memberDetails, OffsetTimeJavaType.class, xmlDocumentContext ); |
| 511 | + return true; |
| 512 | + } |
| 513 | + |
| 514 | + if ( jaxbType.getValue().equalsIgnoreCase( "zoneid" ) |
| 515 | + || jaxbType.getValue().equalsIgnoreCase( "zone_id" ) |
| 516 | + || ZoneId.class.getName().equals( jaxbType.getValue() ) ) { |
| 517 | + applyJavaTypeAnnotation( memberDetails, ZoneIdJavaType.class, xmlDocumentContext ); |
| 518 | + return true; |
| 519 | + } |
| 520 | + |
| 521 | + if ( jaxbType.getValue().equalsIgnoreCase( "zoneoffset" ) |
| 522 | + || jaxbType.getValue().equalsIgnoreCase( "zone_offset" ) |
| 523 | + || ZoneOffset.class.getName().equals( jaxbType.getValue() ) ) { |
| 524 | + applyJavaTypeAnnotation( memberDetails, ZoneOffsetJavaType.class, xmlDocumentContext ); |
| 525 | + return true; |
| 526 | + } |
| 527 | + |
| 528 | + if ( jaxbType.getValue().equalsIgnoreCase( "timestamp" ) |
| 529 | + || jaxbType.getValue().equalsIgnoreCase( "time_stamp" ) |
| 530 | + || java.util.Date.class.getName().equals( jaxbType.getValue() ) |
| 531 | + || Timestamp.class.getName().equals( jaxbType.getValue() ) ) { |
| 532 | + applyJavaTypeAnnotation( memberDetails, DateJavaType.class, xmlDocumentContext ); |
| 533 | + applyTemporalPrecision( memberDetails, TemporalType.TIMESTAMP, xmlDocumentContext ); |
| 534 | + return true; |
| 535 | + } |
| 536 | + |
| 537 | + if ( jaxbType.getValue().equalsIgnoreCase( "date" ) |
| 538 | + || java.sql.Date.class.getName().equals( jaxbType.getValue() ) ) { |
| 539 | + applyJavaTypeAnnotation( memberDetails, DateJavaType.class, xmlDocumentContext ); |
| 540 | + applyTemporalPrecision( memberDetails, TemporalType.DATE, xmlDocumentContext ); |
| 541 | + return true; |
| 542 | + } |
| 543 | + |
| 544 | + if ( jaxbType.getValue().equalsIgnoreCase( "time" ) |
| 545 | + || java.sql.Time.class.getName().equals( jaxbType.getValue() ) ) { |
| 546 | + applyJavaTypeAnnotation( memberDetails, DateJavaType.class, xmlDocumentContext ); |
| 547 | + applyTemporalPrecision( memberDetails, TemporalType.TIME, xmlDocumentContext ); |
| 548 | + return true; |
| 549 | + } |
| 550 | + |
| 551 | + if ( jaxbType.getValue().equalsIgnoreCase( "calendar" ) |
| 552 | + || jaxbType.getValue().equalsIgnoreCase( "gregoriancalendar" ) |
| 553 | + || jaxbType.getValue().equalsIgnoreCase( "gregorian_calendar" ) |
| 554 | + || Calendar.class.getName().equals( jaxbType.getValue() ) |
| 555 | + || GregorianCalendar.class.getName().equals( jaxbType.getValue() ) ) { |
| 556 | + applyJavaTypeAnnotation( memberDetails, CalendarJavaType.class, xmlDocumentContext ); |
| 557 | + return true; |
| 558 | + } |
| 559 | + |
| 560 | + if ( jaxbType.getValue().equalsIgnoreCase( "timezone" ) |
| 561 | + || jaxbType.getValue().equalsIgnoreCase( "time_zone" ) |
| 562 | + || TimeZone.class.getName().equals( jaxbType.getValue() ) ) { |
| 563 | + applyJavaTypeAnnotation( memberDetails, TimeZoneJavaType.class, xmlDocumentContext ); |
| 564 | + return true; |
| 565 | + } |
| 566 | + |
| 567 | + return false; |
| 568 | + } |
| 569 | + |
| 570 | + private static void applyJavaTypeAnnotation( |
| 571 | + MutableMemberDetails memberDetails, |
| 572 | + Class<? extends BasicJavaType<?>> descriptor, |
| 573 | + XmlDocumentContext xmlDocumentContext) { |
| 574 | + final JavaTypeAnnotation javaTypeAnnotation = (JavaTypeAnnotation) memberDetails.applyAnnotationUsage( |
| 575 | + HibernateAnnotations.JAVA_TYPE, |
| 576 | + xmlDocumentContext.getModelBuildingContext() |
| 577 | + ); |
| 578 | + javaTypeAnnotation.value( descriptor ); |
| 579 | + } |
| 580 | + |
| 581 | + private static void applyTemporalPrecision(MutableMemberDetails memberDetails, TemporalType temporalType, XmlDocumentContext xmlDocumentContext) { |
| 582 | + final Temporal directUsage = memberDetails.getDirectAnnotationUsage( Temporal.class ); |
| 583 | + if ( directUsage != null ) { |
| 584 | + // make sure they match |
| 585 | + if ( directUsage.value() != temporalType ) { |
| 586 | + throw new org.hibernate.MappingException( String.format( |
| 587 | + Locale.ROOT, |
| 588 | + "Mismatch in expected TemporalType on %s; found %s and %s", |
| 589 | + memberDetails, |
| 590 | + directUsage.value(), |
| 591 | + temporalType |
| 592 | + ) ); |
| 593 | + } |
| 594 | + return; |
| 595 | + } |
| 596 | + |
| 597 | + final TemporalJpaAnnotation temporalAnnotation = (TemporalJpaAnnotation) memberDetails.applyAnnotationUsage( |
| 598 | + JpaAnnotations.TEMPORAL, |
| 599 | + xmlDocumentContext.getModelBuildingContext() |
| 600 | + ); |
| 601 | + temporalAnnotation.value( temporalType ); |
| 602 | + } |
| 603 | + |
261 | 604 | private static final Parameter[] NO_PARAMETERS = new Parameter[0];
|
262 | 605 |
|
263 | 606 | public static Parameter[] collectParameters(
|
|
0 commit comments