001/* 002 * ============================================================================ 003 * Copyright © 2002-2026 by Thomas Thrien. 004 * All Rights Reserved. 005 * ============================================================================ 006 * Licensed to the public under the agreements of the GNU Lesser General Public 007 * License, version 3.0 (the "License"). You may obtain a copy of the License at 008 * 009 * http://www.gnu.org/licenses/lgpl.html 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, WITHOUT 013 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 014 * License for the specific language governing permissions and limitations 015 * under the License. 016 */ 017 018package org.tquadrat.foundation.javacomposer; 019 020import org.apiguardian.api.API; 021import org.tquadrat.foundation.annotation.ClassVersion; 022import org.tquadrat.foundation.annotation.UtilityClass; 023import org.tquadrat.foundation.exception.PrivateConstructorForStaticClassCalledError; 024import org.tquadrat.foundation.exception.ValidationException; 025import org.tquadrat.foundation.javacomposer.internal.AnnotationSpecImpl; 026import org.tquadrat.foundation.javacomposer.internal.AnnotationSpecImpl.BuilderImpl; 027import org.tquadrat.foundation.javacomposer.internal.AnnotationTypeSpecImpl; 028import org.tquadrat.foundation.javacomposer.internal.AnnotationValueVisitor; 029import org.tquadrat.foundation.javacomposer.internal.ClassNameImpl; 030import org.tquadrat.foundation.javacomposer.internal.ClassSpecImpl; 031import org.tquadrat.foundation.javacomposer.internal.CodeBlockImpl; 032import org.tquadrat.foundation.javacomposer.internal.EnumSpecImpl; 033import org.tquadrat.foundation.javacomposer.internal.FieldSpecImpl; 034import org.tquadrat.foundation.javacomposer.internal.InterfaceSpecImpl; 035import org.tquadrat.foundation.javacomposer.internal.JavaFileImpl; 036import org.tquadrat.foundation.javacomposer.internal.LambdaSpecImpl; 037import org.tquadrat.foundation.javacomposer.internal.MethodSpecImpl; 038import org.tquadrat.foundation.javacomposer.internal.ParameterSpecImpl; 039import org.tquadrat.foundation.javacomposer.internal.RecordSpecImpl; 040import org.tquadrat.foundation.javacomposer.internal.TypeNameImpl; 041import org.tquadrat.foundation.javacomposer.internal.TypeSpecImpl; 042import org.tquadrat.foundation.lang.Lazy; 043import org.tquadrat.foundation.lang.Objects; 044import org.tquadrat.foundation.util.JavaUtils; 045 046import javax.lang.model.SourceVersion; 047import javax.lang.model.element.AnnotationMirror; 048import javax.lang.model.element.ExecutableElement; 049import javax.lang.model.element.Modifier; 050import javax.lang.model.element.TypeElement; 051import javax.lang.model.element.VariableElement; 052import javax.lang.model.type.DeclaredType; 053import javax.lang.model.type.ExecutableType; 054import javax.lang.model.type.TypeVariable; 055import javax.lang.model.util.Types; 056import java.lang.annotation.Annotation; 057import java.lang.reflect.Array; 058import java.lang.reflect.InvocationTargetException; 059import java.lang.reflect.Method; 060import java.lang.reflect.Parameter; 061import java.lang.reflect.Type; 062import java.util.Collection; 063import java.util.LinkedHashSet; 064import java.util.List; 065import java.util.Optional; 066import java.util.function.Supplier; 067 068import static java.lang.String.format; 069import static java.util.Arrays.sort; 070import static java.util.Arrays.stream; 071import static java.util.Comparator.comparing; 072import static java.util.stream.Collectors.joining; 073import static javax.lang.model.element.Modifier.ABSTRACT; 074import static javax.lang.model.element.Modifier.DEFAULT; 075import static javax.lang.model.element.Modifier.FINAL; 076import static javax.lang.model.element.Modifier.PRIVATE; 077import static javax.lang.model.element.Modifier.PUBLIC; 078import static javax.lang.model.element.Modifier.STATIC; 079import static org.apiguardian.api.API.Status.STABLE; 080import static org.tquadrat.foundation.javacomposer.Layout.LAYOUT_DEFAULT; 081import static org.tquadrat.foundation.javacomposer.MethodSpec.CONSTRUCTOR; 082import static org.tquadrat.foundation.javacomposer.Primitives.BOOLEAN; 083import static org.tquadrat.foundation.javacomposer.Primitives.INT; 084import static org.tquadrat.foundation.lang.CommonConstants.EMPTY_STRING; 085import static org.tquadrat.foundation.lang.Objects.deepEquals; 086import static org.tquadrat.foundation.lang.Objects.hash; 087import static org.tquadrat.foundation.lang.Objects.isNull; 088import static org.tquadrat.foundation.lang.Objects.nonNull; 089import static org.tquadrat.foundation.lang.Objects.require; 090import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 091import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument; 092import static org.tquadrat.foundation.lang.Objects.requireValidNonNullArgument; 093import static org.tquadrat.foundation.util.JavaUtils.translateModifiers; 094 095/** 096 * <p>{@summary The factory for the various JavaComposer artefacts.}</p> 097 * <p>Instances of this class do not maintain a state, therefore they are 098 * thread-safe without any synchronisation.</p> 099 * 100 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 101 * @version $Id: JavaComposer.java 1163 2026-03-20 15:28:33Z tquadrat $ 102 * @since 0.2.0 103 * 104 * @UMLGraph.link 105 */ 106@SuppressWarnings( {"OverlyCoupledClass", "ClassWithTooManyMethods", "OverlyComplexClass"} ) 107@ClassVersion( sourceVersion = "$Id: JavaComposer.java 1163 2026-03-20 15:28:33Z tquadrat $" ) 108@API( status = STABLE, since = "0.2.0" ) 109public final class JavaComposer 110{ 111 /*-----------*\ 112 ====** Constants **======================================================== 113 \*-----------*/ 114 /** 115 * The Javadoc tag for inherited documentation: {@value}. 116 */ 117 public static final String JAVADOC_TAG_INHERITDOC = "{@inheritDoc}"; 118 119 /** 120 * The Javadoc tag for a constant value: {@value}. 121 */ 122 public static final String JAVADOC_TAG_VALUE = "{@value}"; 123 124 /** 125 * Message: {@value}. 126 */ 127 public static final String MSG_CannotOverrideFinalClass = "Cannot override method on final class '%s'"; 128 129 /** 130 * Message: {@value}. 131 */ 132 public static final String MSG_CannotOverrideMethod = "Cannot override method with modifier '%s'"; 133 134 /*------------*\ 135 ====** Attributes **======================================================= 136 \*------------*/ 137 /** 138 * If set to {@code true}, some debug information will be added to the 139 * output. 140 */ 141 private final boolean m_AddDebugOutput; 142 143 /** 144 * The {@code @ClassVersion} annotation. 145 */ 146 @SuppressWarnings( "FieldNamingConvention" ) 147 private final Lazy<AnnotationSpec> m_Annotation_ClassVersion; 148 149 /** 150 * The {@code @UtilityClass} annotation. 151 */ 152 @SuppressWarnings( "FieldNamingConvention" ) 153 private final Lazy<AnnotationSpec> m_Annotation_UtilityClass; 154 155 /** 156 * The Javadoc comment for an overriding method. 157 */ 158 @SuppressWarnings( "FieldNamingConvention" ) 159 private final Lazy<CodeBlock> m_JavaDoc_InheritDoc; 160 161 /** 162 * The layout that is used to format the output. 163 */ 164 private final Layout m_Layout; 165 166 /** 167 * The maximum number of fields for a class; if this number is exceeded, 168 * an annotation 169 * {@link SuppressWarnings @SuppressWarnings} 170 * with 171 * {@link SuppressableWarnings#CLASS_WITH_TOO_MANY_FIELDS} 172 * will be added to the class. 173 */ 174 @SuppressWarnings( "MagicNumber" ) 175 private int m_MaxFields = 16; 176 177 /** 178 * The maximum number of methods for a class; if this number is exceeded, 179 * an annotation 180 * {@link SuppressWarnings @SuppressWarnings} 181 * with 182 * {@link SuppressableWarnings#CLASS_WITH_TOO_MANY_METHODS} 183 * will be added to the class. 184 */ 185 @SuppressWarnings( "MagicNumber" ) 186 private int m_MaxMethods = 20; 187 188 /** 189 * A predefined {@code equals()} method. 190 */ 191 @SuppressWarnings( "FieldNamingConvention" ) 192 private final Lazy<MethodSpec> m_Method_Equals; 193 194 /** 195 * A predefined {@code hashCode()} method. 196 */ 197 @SuppressWarnings( "FieldNamingConvention" ) 198 private final Lazy<MethodSpec> m_Method_HashCode; 199 200 /** 201 * A predefined {@code toString()} method. 202 */ 203 @SuppressWarnings( "FieldNamingConvention" ) 204 private final Lazy<MethodSpec> m_Method_ToString; 205 206 /*--------------*\ 207 ====** Constructors **===================================================== 208 \*--------------*/ 209 /** 210 * Creates a new instance of {@code JavaComposer} that uses the default 211 * {@link Layout#LAYOUT_DEFAULT Layout}. 212 */ 213 public JavaComposer() 214 { 215 this( LAYOUT_DEFAULT, false ); 216 } // JavaComposer() 217 218 /** 219 * Creates a new instance of {@code JavaComposer}. 220 * 221 * @param layout The layout that is used to format the output. 222 */ 223 public JavaComposer( final Layout layout ) 224 { 225 this( layout, false ); 226 } // JavaComposer() 227 228 /** 229 * Creates a new instance of {@code JavaComposer} that uses the default 230 * {@link Layout#LAYOUT_DEFAULT Layout} and adds some debug information 231 * to the output. 232 * 233 * @param addDebugOutput {@code true} if debug information should be 234 * added, {@code false} if not. 235 */ 236 public JavaComposer( final boolean addDebugOutput ) 237 { 238 this( LAYOUT_DEFAULT, addDebugOutput ); 239 } // JavaComposer() 240 241 /** 242 * Creates a new instance of {@code JavaComposer}. 243 * 244 * @param layout The layout that is used to format the output. 245 * @param addDebugOutput {@code true} if debug information should be 246 * added, {@code false} if not. 247 */ 248 public JavaComposer( final Layout layout, final boolean addDebugOutput ) 249 { 250 m_Layout = requireNonNullArgument( layout, "layout" ); 251 m_AddDebugOutput = addDebugOutput; 252 253 //---* The inherited doc comment *------------------------------------- 254 final var codeBlockSupplier = (Supplier<CodeBlock>) () -> 255 { 256 final var retValue = new CodeBlockImpl.BuilderImpl( this ) 257 .addWithoutDebugInfo( "$L\n", JAVADOC_TAG_INHERITDOC ) 258 .build(); 259 260 //---* Done *------------------------------------------------------ 261 return retValue; 262 }; 263 m_JavaDoc_InheritDoc = Lazy.use( codeBlockSupplier ); 264 final var inheritDocComment = codeBlockSupplier.get(); 265 266 //---* The @ClassVersion annotation *---------------------------------- 267 final var annotationSpecSupplierClassVersion = (Supplier<AnnotationSpec>) () -> 268 { 269 final var retValue = annotationBuilder( ClassVersion.class ) 270 .forceInline( true ) 271 .addMember( "sourceVersion", "$S", "Generated with JavaComposer" ) 272 .addMember( "isGenerated", "$L", "true" ) 273 .build(); 274 275 //---* Done *------------------------------------------------------ 276 return retValue; 277 }; 278 m_Annotation_ClassVersion = Lazy.use( annotationSpecSupplierClassVersion ); 279 280 //---* The @UtilityClass annotation *---------------------------------- 281 final var annotationSpecSupplierUtilityClass = (Supplier<AnnotationSpec>) () -> 282 { 283 final var retValue = annotationBuilder( UtilityClass.class ) 284 .forceInline( true ) 285 .build(); 286 287 //---* Done *------------------------------------------------------ 288 return retValue; 289 }; 290 m_Annotation_UtilityClass = Lazy.use( annotationSpecSupplierUtilityClass ); 291 292 //---* The equals() method *------------------------------------------- 293 var methodSpecSupplier = (Supplier<MethodSpec>) () -> 294 { 295 final var retValue = methodBuilder( "equals" ) 296 .addJavadoc( inheritDocComment ) 297 .addAnnotation( Override.class ) 298 .addModifiers( PUBLIC ) 299 .addParameter( Object.class, "o", FINAL ) 300 .returns( BOOLEAN ) 301 .build(); 302 303 //---* Done *------------------------------------------------------ 304 return retValue; 305 }; 306 m_Method_Equals = Lazy.use( methodSpecSupplier ); 307 308 //---* The hashCode() method *----------------------------------------- 309 methodSpecSupplier = () -> 310 { 311 final var retValue = methodBuilder( "hashCode" ) 312 .addJavadoc( inheritDocComment ) 313 .addAnnotation( Override.class ) 314 .addModifiers( PUBLIC ) 315 .returns( INT ) 316 .build(); 317 318 //---* Done *------------------------------------------------------ 319 return retValue; 320 }; 321 m_Method_HashCode = Lazy.use( methodSpecSupplier ); 322 323 //---* The toString() method *----------------------------------------- 324 methodSpecSupplier = () -> 325 { 326 final var retValue = methodBuilder( "toString" ) 327 .addJavadoc( inheritDocComment ) 328 .addAnnotation( Override.class ) 329 .addModifiers( PUBLIC ) 330 .returns( String.class ) 331 .build(); 332 333 //---* Done *------------------------------------------------------ 334 return retValue; 335 }; 336 m_Method_ToString = Lazy.use( methodSpecSupplier ); 337 } // JavaComposer() 338 339 /*---------*\ 340 ====** Methods **========================================================== 341 \*---------*/ 342 /** 343 * Returns the flag that controls whether the output should be enhanced 344 * with some debug information. 345 * 346 * @return {@code true} if the debug information should be added to the 347 * output, {@code false} otherwise. 348 */ 349 @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" ) 350 public final boolean addDebugOutput() { return m_AddDebugOutput; } 351 352 /** 353 * <p>{@summary Creates a builder for an instance of an implementation for 354 * {@link AnnotationSpec} 355 * from the given 356 * {@link ClassName} 357 * instance.}</p> 358 * <p>This builder generates the code for the <i>use</i> of an annotation, 359 * while the builders returned from 360 * {@link #annotationTypeBuilder(CharSequence)} 361 * and 362 * {@link #annotationTypeBuilder(ClassName)} 363 * will generate <i>new</i> annotation types.</p> 364 * 365 * @param type The class name. 366 * @return The new builder. 367 */ 368 public final AnnotationSpec.Builder annotationBuilder( final ClassName type ) 369 { 370 final var retValue = new BuilderImpl( this, requireNonNullArgument( type, "type" ) ); 371 372 //---* Done *---------------------------------------------------------- 373 return retValue; 374 } // annotationBuilder() 375 376 /** 377 * <p>{@summary Creates a builder for an instance of an implementation for 378 * {@link AnnotationSpec} 379 * from the given 380 * {@link Class} 381 * instance.}</p> 382 * <p>This builder generates the code for the <i>use</i> of an annotation, 383 * while the builders returned from 384 * {@link #annotationTypeBuilder(CharSequence)} 385 * and 386 * {@link #annotationTypeBuilder(ClassName)} 387 * will generate <i>new</i> annotation types.</p> 388 * 389 * @param type The class. 390 * @return The new builder. 391 */ 392 public final AnnotationSpec.Builder annotationBuilder( final Class<?> type ) 393 { 394 final var retValue = annotationBuilder( ClassName.from( type ) ); 395 396 //---* Done *---------------------------------------------------------- 397 return retValue; 398 } // annotationBuilder() 399 400 /** 401 * <p>{@summary Creates a builder for an annotation.}</p> 402 * <p>This method creates a builder for a new annotation, while 403 * {@link #annotationBuilder(ClassName)} 404 * and 405 * {@link #annotationBuilder(Class)} 406 * will create the builder for the <i>use</i> of an annotation.</p> 407 * 408 * @param className The name of the annotation. 409 * @return The builder. 410 */ 411 public final TypeSpec.Builder annotationTypeBuilder( final ClassName className ) 412 { 413 final var retValue = annotationTypeBuilder( requireNonNullArgument( className, "className" ).simpleName() ); 414 415 //---* Done *---------------------------------------------------------- 416 return retValue; 417 } // annotationTypeBuilder() 418 419 /** 420 * <p>{@summary Creates a builder for an annotation.}</p> 421 * <p>This method creates a builder for a new annotation, while 422 * {@link #annotationBuilder(ClassName)} 423 * and 424 * {@link #annotationBuilder(Class)} 425 * will create the builder for the <i>use</i> of an annotation.</p> 426 * 427 * @param name The name of the annotation. 428 * @return The builder. 429 */ 430 public final TypeSpec.Builder annotationTypeBuilder( final CharSequence name ) 431 { 432 final var retValue = new AnnotationTypeSpecImpl.BuilderImpl( this, requireNonNullArgument( name, "name" ) ); 433 434 //---* Done *---------------------------------------------------------- 435 return retValue; 436 } // annotationTypeBuilder() 437 438 /** 439 * Creates a builder for an anonymous class. 440 * 441 * @param typeArguments The type arguments. 442 * @return The builder. 443 */ 444 public final TypeSpec.Builder anonymousClassBuilder( final CodeBlock typeArguments ) 445 { 446 final var retValue = new ClassSpecImpl.BuilderImpl( this, (CodeBlockImpl) requireNonNullArgument( typeArguments, "typeArguments" ) ); 447 448 //---* Done *---------------------------------------------------------- 449 return retValue; 450 } // anonymousClassBuilder() 451 452 /** 453 * <p>{@summary Creates a builder for an anonymous class.} This method is 454 * the shortcut for</p> 455 * <pre><code> CodeBlock codeBlock = codeBlockBuilder() 456 * .add( format, args ) 457 * .build(); 458 * TypeSpec.Builder builder = anonymousClassBuilder( codeBlock ); </code></pre> 459 * 460 * @param format The format for the code; may be empty. 461 * @param args The arguments for the code. 462 * @return The builder. 463 * 464 * @see #codeBlockBuilder() 465 * @see #anonymousClassBuilder(CodeBlock) 466 */ 467 public final TypeSpec.Builder anonymousClassBuilder( final String format, final Object... args ) 468 { 469 final var retValue = anonymousClassBuilder( codeBlockBuilder() 470 .add( format, args ) 471 .build() ); 472 473 //---* Done *---------------------------------------------------------- 474 return retValue; 475 } // anonymousClassBuilder() 476 477 /** 478 * Creates a builder for a regular class. 479 * 480 * @param className The name of the class. 481 * @return The builder. 482 */ 483 public final TypeSpec.Builder classBuilder( final ClassName className ) 484 { 485 final var retValue = classBuilder( requireNonNullArgument( className, "className" ).simpleName() ); 486 487 //---* Done *---------------------------------------------------------- 488 return retValue; 489 } // classBuilder() 490 491 /** 492 * Creates a builder for a regular class. 493 * 494 * @param name The name of the class. 495 * @return The builder. 496 */ 497 public final TypeSpec.Builder classBuilder( final CharSequence name ) 498 { 499 final var retValue = new ClassSpecImpl.BuilderImpl( this, requireNotEmptyArgument( name, "name" ), null ); 500 501 //---* Done *---------------------------------------------------------- 502 return retValue; 503 } // classBuilder() 504 505 /** 506 * Creates a builder for an instance of 507 * {@link CodeBlock}. 508 * 509 * @return The new builder. 510 */ 511 public final CodeBlock.Builder codeBlockBuilder() 512 { 513 return new CodeBlockImpl.BuilderImpl( this ); 514 } // codeBlockBuilder() 515 516 /** 517 * Creates a new 518 * {@link CodeBlock} 519 * instance from the given format and arguments. 520 * 521 * @note No debug info will be added. 522 * 523 * @param format The format. 524 * @param args The arguments. 525 * @return The new code block. 526 */ 527 public final CodeBlock codeBlockOf( final String format, final Object... args ) 528 { 529 final var retValue = ((CodeBlockImpl.BuilderImpl) codeBlockBuilder()) 530 .add( format, args ) 531 .build(); 532 533 //---* Done *---------------------------------------------------------- 534 return retValue; 535 } // codeBlockOf() 536 537 /** 538 * Creates a builder that builds an instance of {@code MethodSpec} for a 539 * constructor. 540 * 541 * @return The builder. 542 */ 543 public final MethodSpec.Builder constructorBuilder() 544 { 545 final var retValue = new MethodSpecImpl.BuilderImpl( this, CONSTRUCTOR ); 546 547 //---* Done *---------------------------------------------------------- 548 return retValue; 549 } // constructorBuilder() 550 551 /** 552 * Creates an instance of 553 * {@link AnnotationSpec} 554 * from the given 555 * {@link Annotation} 556 * instance. 557 * 558 * @param annotation The annotation. 559 * @return The new instance of {@code AnnotationSpec}. 560 */ 561 public final AnnotationSpec createAnnotation( final Annotation annotation ) 562 { 563 final var retValue = createAnnotation( annotation, false ); 564 565 //---* Done *---------------------------------------------------------- 566 return retValue; 567 } // createAnnotation() 568 569 /** 570 * Creates an instance of 571 * {@link AnnotationSpec} 572 * from the given 573 * {@link Annotation} 574 * instance. 575 * 576 * @param annotation The annotation. 577 * @param includeDefaultValues {@code true} to include the 578 * annotation's default values, {@code false} to ignore them. 579 * @return The new instance of {@code AnnotationSpec}. 580 */ 581 public final AnnotationSpec createAnnotation( final Annotation annotation, final boolean includeDefaultValues ) 582 { 583 final var builder = (AnnotationSpecImpl.BuilderImpl) annotationBuilder( ClassName.from( requireNonNullArgument( annotation, "annotation" ).annotationType() ) ); 584 try 585 { 586 final var methods = annotation.annotationType().getDeclaredMethods(); 587 sort( methods, comparing( Method::getName ) ); 588 MethodsLoop: for( final var method : methods ) 589 { 590 final var value = method.invoke( annotation ); 591 if( !includeDefaultValues ) 592 { 593 if( deepEquals( value, method.getDefaultValue() ) ) continue MethodsLoop; 594 } 595 if( value.getClass().isArray() ) 596 { 597 for( var i = 0; i < Array.getLength( value ); ++i ) 598 { 599 builder.addMemberForValue( method.getName(), Array.get( value, i ) ); 600 } 601 continue MethodsLoop; 602 } 603 if( value instanceof final Annotation annotationValue ) 604 { 605 builder.addMember( method.getName(), "$L", createAnnotation( annotationValue, false ) ); 606 continue MethodsLoop; 607 } 608 builder.addMemberForValue( method.getName(), value ); 609 } // MethodsLoop: 610 } 611 catch( final SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e ) 612 { 613 throw new JavaComposerException( "Reflecting " + annotation + " failed!", e ); 614 } 615 final var retValue = builder.build(); 616 617 //---* Done *---------------------------------------------------------- 618 return retValue; 619 } // createAnnotation() 620 621 /** 622 * Creates an instance of 623 * {@link AnnotationSpec} 624 * from the given 625 * {@link AnnotationMirror} 626 * instance. 627 * 628 * @param annotation The annotation mirror. 629 * @return The new instance of {@code AnnotationSpec}. 630 */ 631 public final AnnotationSpec createAnnotation( final AnnotationMirror annotation ) 632 { 633 final var element = (TypeElement) requireNonNullArgument( annotation, "annotation" ).getAnnotationType().asElement(); 634 final var builder = (AnnotationSpecImpl.BuilderImpl) annotationBuilder( ClassName.from( element ) ); 635 final var visitor = new AnnotationValueVisitor( builder ); 636 for( final var executableElement : annotation.getElementValues().keySet() ) 637 { 638 final var name = executableElement.getSimpleName().toString(); 639 @SuppressWarnings( "unlikely-arg-type" ) 640 final var value = annotation.getElementValues().get( executableElement ); 641 value.accept( visitor, name ); 642 } 643 final var retValue = builder.build(); 644 645 //---* Done *---------------------------------------------------------- 646 return retValue; 647 } // createAnnotation() 648 649 /** 650 * Returns a {@code @ClassVersion} annotation with a default text for 651 * {@link ClassVersion#sourceVersion()}. 652 * 653 * @return The annotation. 654 * 655 * @see ClassVersion 656 */ 657 public final AnnotationSpec createClassVersionAnnotation() { return m_Annotation_ClassVersion.get(); } 658 659 /** 660 * Returns a {@code @ClassVersion} annotation with the text for 661 * {@link ClassVersion#sourceVersion()}. 662 * 663 * @param sourceVersion The text. 664 * @return The annotation. 665 * 666 * @see ClassVersion 667 */ 668 public final AnnotationSpec createClassVersionAnnotation( final String sourceVersion ) 669 { 670 final var retValue = annotationBuilder( ClassVersion.class ) 671 .forceInline( true ) 672 .addMember( "sourceVersion", "$S", requireNotEmptyArgument( sourceVersion, "sourceVersion" ) ) 673 .addMember( "isGenerated", "$L", "true" ) 674 .build(); 675 676 //---* Done *------------------------------------------------------ 677 return retValue; 678 } // createClassVersionAnnotation() 679 680 /** 681 * Creates the comment for a constant. 682 * 683 * @param comment The already existing comment; can be {@code null}. 684 * @return The comment for the new constant. 685 */ 686 private final CodeBlock createComment4Constant( final CodeBlock comment ) 687 { 688 var retValue = comment; 689 if( isNull( retValue ) ) 690 { 691 retValue = codeBlockOf( 692 """ 693 Constant value: $L. 694 """, JAVADOC_TAG_VALUE ); 695 } 696 else 697 { 698 if( !comment.toString().contains( JAVADOC_TAG_VALUE ) ) 699 { 700 retValue = retValue.join( "\n", codeBlockOf( 701 """ 702 <p>The value is: $L.</p> 703 """, JAVADOC_TAG_VALUE ) ); 704 } 705 } 706 707 //---* Done *---------------------------------------------------------- 708 return retValue; 709 } // createComment4Constant() 710 711 /** 712 * <p>{@summary Creates a String constant.}</p> 713 * <p>A constant is an initialised {@code public static final} field.</p> 714 * 715 * @param name The name of the constant. 716 * @param value The value of the constant. 717 * @param comment The description for the constant. 718 * @return The field spec for the new constant. 719 */ 720 public final FieldSpec createConstant( final CharSequence name, final String value, final CodeBlock comment ) 721 { 722 final var retValue = createConstant( name, String.class, codeBlockOf( "$S", requireNonNullArgument( value, "value" ) ), createComment4Constant( comment ) ); 723 724 //---* Done *---------------------------------------------------------- 725 return retValue; 726 } // createConstant() 727 728 /** 729 * <p>{@summary Creates a numerical constant.}</p> 730 * <p>A constant is an initialised {@code public static final} field.</p> 731 * 732 * @param name The name of the constant. 733 * @param type The type of the constant. 734 * @param value The value of the constant. 735 * @param comment The description for the constant. 736 * @return The field spec for the new constant. 737 */ 738 @SuppressWarnings( "SameParameterValue" ) 739 private final FieldSpec createConstant( final CharSequence name, final TypeName type, final Number value, final CodeBlock comment ) 740 { 741 final var retValue = createConstant( name, type, codeBlockOf( "$L", requireNonNullArgument( value, "value" ) ), createComment4Constant( comment ) ); 742 743 //---* Done *---------------------------------------------------------- 744 return retValue; 745 } // createConstant() 746 747 /** 748 * <p>{@summary Creates an integer constant.}</p> 749 * <p>A constant is an initialised {@code public static final} field.</p> 750 * 751 * @param name The name of the constant. 752 * @param value The value of the constant. 753 * @param comment The description for the constant. 754 * @return The field spec for the new constant. 755 */ 756 public final FieldSpec createConstant( final CharSequence name, final int value, final CodeBlock comment ) 757 { 758 final var retValue = createConstant( name, INT, Integer.valueOf( value ), comment ); 759 760 //---* Done *---------------------------------------------------------- 761 return retValue; 762 } // createConstant() 763 764 /** 765 * <p>{@summary Creates a constant.}</p> 766 * <p>A constant is an initialised {@code public static final} field.</p> 767 * 768 * @param name The name of the constant. 769 * @param type The type of the constant. 770 * @param value The value of the constant. 771 * @param comment The description for the constant. 772 * @return The field spec for the new constant. 773 */ 774 public final FieldSpec createConstant( final CharSequence name, final Type type, final CodeBlock value, final CodeBlock comment ) 775 { 776 return createConstant( name, TypeName.from( requireNonNullArgument( type, "type" ) ), value, comment ); 777 } // createConstant() 778 779 /** 780 * Creates a constant.<br> 781 * <br>A constant is an initialised {@code public final static} field. 782 * 783 * @param name The name of the constant. 784 * @param type The type of the constant. 785 * @param value The value of the constant. 786 * @param comment The description for the constant. 787 * @return The field spec for the new constant. 788 */ 789 public final FieldSpec createConstant( final CharSequence name, final TypeName type, final CodeBlock value, final CodeBlock comment ) 790 { 791 final var builder = fieldBuilder( type, name, PUBLIC, FINAL, STATIC ) 792 .initializer( requireNonNullArgument( value, "value" ) ); 793 794 if( nonNull( comment ) && !comment.isEmpty() ) builder.addJavadoc( comment ); 795 796 final var retValue = builder.build(); 797 798 //---* Done *---------------------------------------------------------- 799 return retValue; 800 } // createConstant() 801 802 /** 803 * <p>{@summary Returns a builder for an implementation of the method 804 * {@link Object#equals(Object)} 805 * that just needs the method body for completion.}</p> 806 * <p>The argument has the name "{@code o}".</p> 807 * 808 * @return The method builder. 809 */ 810 public final MethodSpec.Builder createEqualsBuilder() 811 { 812 final var retValue = m_Method_Equals.get().toBuilder(); 813 814 //---* Done *---------------------------------------------------------- 815 return retValue; 816 } // createEqualsBuilder() 817 818 /** 819 * <p>{@summary Returns a 820 * {@link MethodSpec} 821 * instance for an implementation of the method 822 * {@link Object#equals(Object)}.}</p> 823 * <p>The argument has the name "{@code o}".</p> 824 * 825 * @param body The method body. 826 * @return The method specification. 827 */ 828 public final MethodSpec createEqualsMethod( final CodeBlock body ) 829 { 830 final var retValue = createEqualsBuilder() 831 .addCode( requireNonNullArgument( body, "body" ) ) 832 .build(); 833 834 //---* Done *---------------------------------------------------------- 835 return retValue; 836 } // createEqualsMethod() 837 838 /** 839 * Returns a builder for an implementation of the method 840 * {@link Object#hashCode()} 841 * that just needs the method body for completion. 842 * 843 * @return The method builder. 844 */ 845 public final MethodSpec.Builder createHashCodeBuilder() 846 { 847 final var retValue = m_Method_HashCode.get().toBuilder(); 848 849 //---* Done *---------------------------------------------------------- 850 return retValue; 851 } // createHashCodeBuilder() 852 853 /** 854 * Returns a 855 * {@link MethodSpec} 856 * instance for an implementation of the method 857 * {@link Object#hashCode()}. 858 * 859 * @param body The method body. 860 * @return The method specification. 861 */ 862 public final MethodSpec createHashCodeMethod( final CodeBlock body ) 863 { 864 final var retValue = createHashCodeBuilder() 865 .addCode( requireNonNullArgument( body, "body" ) ) 866 .build(); 867 868 //---* Done *---------------------------------------------------------- 869 return retValue; 870 } // createHashCodeMethod() 871 872 /** 873 * <p>{@summary Returns a 874 * {@link MethodSpec} 875 * instance for an implementation of the method 876 * {@link Object#hashCode()}.}</p> 877 * <p>The created method uses 878 * {@link org.tquadrat.foundation.lang.Objects#hash(Object...)} 879 * for the implementation.</p> 880 * 881 * @param fields The fields that are used for the calculation of the 882 * hash code value. 883 * @return The method specification. 884 */ 885 public final MethodSpec createHashCodeMethod( final FieldSpec... fields ) 886 { 887 final var retValue = createHashCodeMethod( List.of( requireNonNullArgument( fields, "fields" ) ) ); 888 889 //---* Done *---------------------------------------------------------- 890 return retValue; 891 } // createHashCodeMethod() 892 893 /** 894 * <p>{@summary Returns a 895 * {@link MethodSpec} 896 * instance for an implementation of the method 897 * {@link Object#hashCode()}.}</p> 898 * <p>The created method uses 899 * {@link org.tquadrat.foundation.lang.Objects#hash(Object...)} 900 * for the implementation.</p> 901 * 902 * @param fields The fields that are used for the calculation of the 903 * hash code value. 904 * @return The method specification. 905 */ 906 public final MethodSpec createHashCodeMethod( final Collection<? extends FieldSpec> fields ) 907 { 908 final var fieldList = requireNotEmptyArgument( fields, "fields" ).stream() 909 .map( FieldSpec::name ) 910 .collect( joining( ", ", "return hash( ", " )" ) ); 911 final var body = codeBlockBuilder() 912 .addStaticImport( Objects.class, "hash" ) 913 .addStatement( fieldList ) 914 .build(); 915 final var retValue = createHashCodeBuilder() 916 .addCode( body ) 917 .build(); 918 919 //---* Done *---------------------------------------------------------- 920 return retValue; 921 } // createHashCodeMethod() 922 923 /** 924 * Returns a code block with a comment for overriding methods: 925 * <div class="source-container"><pre>/** 926 * * {@inheritDoc} 927 * */</pre></div> 928 * 929 * @return The comment. 930 */ 931 public final CodeBlock createInheritDocComment() { return m_JavaDoc_InheritDoc.get(); } 932 933 /** 934 * <p>{@summary Creates a 935 * {@link MethodSpec} 936 * for the given 937 * {@link ExecutableElement}.}</p> 938 * <p>This method copies visibility modifiers, type parameters, return 939 * type, name, parameters, and {@code throws} declarations, but not the 940 * body (if any).</p> 941 * 942 * @note The annotations will not be copied and must be added separately. 943 * 944 * @param method The method to override. 945 * @return The builder. 946 */ 947 public final MethodSpec createMethod( final ExecutableElement method ) 948 { 949 final var methodName = requireNonNullArgument( method, "method" ).getSimpleName(); 950 final var builder = methodBuilder( methodName ) 951 .addModifiers( method.getModifiers() ) 952 .returns( TypeName.from( method.getReturnType() ) ) 953 .addParameters( parametersOf( method ) ) 954 .varargs( method.isVarArgs() ); 955 956 for( final var typeParameterElement : method.getTypeParameters() ) 957 { 958 final var typeVariable = (TypeVariable) typeParameterElement.asType(); 959 builder.addTypeVariable( TypeVariableName.from( typeVariable ) ); 960 } 961 962 for( final var thrownType : method.getThrownTypes() ) 963 { 964 builder.addException( TypeName.from( thrownType ) ); 965 } 966 967 //---* Create the return value *--------------------------------------- 968 final var retValue = builder.build(); 969 970 //---* Done *---------------------------------------------------------- 971 return retValue; 972 } // createMethod() 973 974 /** 975 * Creates an instance of 976 * {@link ParameterSpec} 977 * from the given 978 * {@link VariableElement} 979 * instance. 980 * 981 * @param element The variable element. 982 * @return The parameter spec. 983 */ 984 public final ParameterSpec createParameter( final VariableElement element ) 985 { 986 final var type = TypeName.from( requireNonNullArgument( element, "element" ).asType() ); 987 final var name = element.getSimpleName().toString(); 988 final var modifiers = element.getModifiers(); 989 final var builder = parameterBuilder( type, name ) 990 .addModifiers( modifiers ); 991 for( final var mirror : element.getAnnotationMirrors() ) 992 { 993 builder.addAnnotation( createAnnotation( mirror ) ); 994 } 995 final var retValue = builder.build(); 996 997 //---* Done *---------------------------------------------------------- 998 return retValue; 999 } // createParameter() 1000 1001 /** 1002 * Creates an instance of 1003 * {@link ParameterSpec} 1004 * from the given 1005 * {@link Parameter} 1006 * instance. 1007 * 1008 * @param parameter The variable element. 1009 * @return The parameter spec. 1010 */ 1011 public final ParameterSpec createParameter( final Parameter parameter ) 1012 { 1013 final var type = TypeName.from( requireNonNullArgument( parameter, "parameter" ).getType() ); 1014 final var name = parameter.getName(); 1015 final var modifiers = translateModifiers( parameter.getModifiers() ); 1016 final var builder = parameterBuilder( type, name ) 1017 .addModifiers( modifiers ); 1018 for( final var annotation : parameter.getAnnotations() ) 1019 { 1020 builder.addAnnotation( createAnnotation( annotation ) ); 1021 } 1022 final var retValue = builder.build(); 1023 1024 //---* Done *---------------------------------------------------------- 1025 return retValue; 1026 } // createParameter() 1027 1028 /** 1029 * Creates a return statement with a comment, using {@code retValue} as 1030 * the name for the return variable. 1031 * 1032 * @return The return statement. 1033 */ 1034 public final CodeBlock createReturnStatement() { return createReturnStatement( "retValue" ); } 1035 1036 /** 1037 * Creates a return statement with a comment. 1038 * 1039 * @param name The name of the variable that is returned. 1040 * @return The return statement. 1041 */ 1042 public final CodeBlock createReturnStatement( final CharSequence name ) 1043 { 1044 final var retValue = new CodeBlockImpl.BuilderImpl( this ) 1045 .addWithoutDebugInfo( 1046 """ 1047 1048 //---* Done *---------------------------------------------------------- 1049 """ ) 1050 .addStatement( "return $L", requireNotEmptyArgument( name, "name" ) ) 1051 .build(); 1052 1053 //---* Done *---------------------------------------------------------- 1054 return retValue; 1055 } // createReturnStatement() 1056 1057 /** 1058 * <p>{@summary Returns a builder for a static class.}</p> 1059 * <p>A <i>static class</i> is a {@code final} class with a 1060 * {@code private} constructor that has only {@code static} members; no 1061 * instances are allowed for such a class, so none of the {@code static} 1062 * methods are factories for that class.</p> 1063 * <p>This would be the skeleton for the new static class:</p> 1064 * <pre><code> <package <i>what.ever.package.was.chosen</i>>; 1065 * 1066 * import org.tquadrat.foundation.annotation.ClassVersion; 1067 * import org.tquadrat.foundation.annotation.UtilityClass; 1068 * import org.tquadrat.foundation.exception.PrivateConstructorForStaticClassCalledError; 1069 * 1070 * @UtilityClass 1071 * @ClassVersion(sourceVersion = "Generated with JavaComposer", isGenerated = true) 1072 * public final class StaticClass { 1073 * /** 1074 * * No instance allowed for this class! 1075 * */ 1076 * private StaticClass() { 1077 * throw new PrivateConstructorForStaticClassCalledError( StaticClass.class ); 1078 * } 1079 * }</code></pre> 1080 * 1081 * @param className The name of the new class. 1082 * @return The builder. 1083 * 1084 * @see ClassVersion 1085 * @see PrivateConstructorForStaticClassCalledError 1086 * @see UtilityClass 1087 */ 1088 public final TypeSpec.Builder createStaticClassBuilder( final CharSequence className ) 1089 { 1090 requireValidNonNullArgument( className, "className", v -> SourceVersion.isName( requireNotEmptyArgument( v, "className" ) ), _ -> "not a valid name: %s".formatted( className ) ); 1091 final var constructor = constructorBuilder() 1092 .addModifiers( PRIVATE ) 1093 .addJavadoc( 1094 """ 1095 No instance allowed for this class! 1096 """ ) 1097 .addStatement( "throw new $T( $L.class )", PrivateConstructorForStaticClassCalledError.class, className ) 1098 .build(); 1099 final var retValue = classBuilder( className ) 1100 .addModifiers( PUBLIC, FINAL ) 1101 .addAnnotation( createUtilityClassAnnotation() ) 1102 .addAnnotation( createClassVersionAnnotation() ) 1103 .addMethod( constructor ); 1104 1105 //---* Done *---------------------------------------------------------- 1106 return retValue; 1107 } // createStaticClassBuilder() 1108 1109 /** 1110 * <p>{@summary Returns a 1111 * {@link SuppressWarnings @SuppressWarnings} 1112 * annotation with the given values.} No annotation will be created if 1113 * the given collection is empty.</p> 1114 * 1115 * @param warnings The warnings to suppress. 1116 * @return An instance of 1117 * {@link Optional} 1118 * that holds the new annotation. 1119 */ 1120 public final Optional<AnnotationSpec> createSuppressWarningsAnnotation( final Collection<SuppressableWarnings> warnings ) 1121 { 1122 return createSuppressWarningsAnnotation( requireNonNullArgument( warnings, "warnings" ).toArray( SuppressableWarnings []::new ) ); 1123 } // createSuppressWarningsAnnotation() 1124 1125 /** 1126 * <p>{@summary Returns a 1127 * {@link SuppressWarnings @SuppressWarnings} 1128 * annotation with the given values.} No annotation will be created if 1129 * the given collection is empty.</p> 1130 * 1131 * @param warnings The warnings to suppress. 1132 * @return An instance of 1133 * {@link Optional} 1134 * that holds the new annotation. 1135 */ 1136 public final Optional<AnnotationSpec> createSuppressWarningsAnnotation( final SuppressableWarnings... warnings ) 1137 { 1138 final Optional<AnnotationSpec> retValue = warnings.length > 0 1139 ? Optional.of( SuppressableWarnings.createSuppressWarningsAnnotation( this, warnings ) ) 1140 : Optional.empty(); 1141 1142 //---* Done *---------------------------------------------------------- 1143 return retValue; 1144 } // createSuppressWarningsAnnotation() 1145 1146 /** 1147 * Returns a builder for an implementation of the method 1148 * {@link Object#toString()} 1149 * that just needs the method body for completion. 1150 * 1151 * @return The method builder. 1152 */ 1153 public final MethodSpec.Builder createToStringBuilder() 1154 { 1155 final var retValue = m_Method_ToString.get().toBuilder(); 1156 1157 //---* Done *---------------------------------------------------------- 1158 return retValue; 1159 } // createToStringBuilder() 1160 1161 /** 1162 * Returns a 1163 * {@link MethodSpec} 1164 * instance for an implementation of the method 1165 * {@link Object#toString()}. 1166 * 1167 * @param body The method body. 1168 * @return The method specification. 1169 */ 1170 public final MethodSpec createToStringMethod( final CodeBlock body ) 1171 { 1172 final var retValue = createToStringBuilder() 1173 .addCode( requireNonNullArgument( body, "body" ) ) 1174 .build(); 1175 1176 //---* Done *---------------------------------------------------------- 1177 return retValue; 1178 } // createToStringMethod() 1179 1180 /** 1181 * Returns a {@code @UtilityClass} annotation. 1182 * 1183 * @return The annotation. 1184 * 1185 * @see UtilityClass 1186 */ 1187 public final AnnotationSpec createUtilityClassAnnotation() { return m_Annotation_UtilityClass.get(); } 1188 1189 /** 1190 * Returns an empty 1191 * {@link CodeBlock}. 1192 * 1193 * @return An empty code block. 1194 */ 1195 public final CodeBlock emptyCodeBlock() { return codeBlockBuilder().build(); } 1196 1197 /** 1198 * Creates a builder for an {@code enum} type. 1199 * 1200 * @param className The name of the class. 1201 * @return The builder. 1202 */ 1203 public final TypeSpec.Builder enumBuilder( final ClassName className ) 1204 { 1205 final var retValue = enumBuilder( requireNonNullArgument( className, "className" ).simpleName() ); 1206 1207 //---* Done *---------------------------------------------------------- 1208 return retValue; 1209 } // enumBuilder() 1210 1211 /** 1212 * Creates a builder for an {@code enum} type. 1213 * 1214 * @param name The name of the class. 1215 * @return The builder. 1216 */ 1217 public final TypeSpec.Builder enumBuilder( final CharSequence name ) 1218 { 1219 final var retValue = new EnumSpecImpl.BuilderImpl( this, requireNonNullArgument( name, "name" ) ); 1220 1221 //---* Done *---------------------------------------------------------- 1222 return retValue; 1223 } // enumBuilder() 1224 1225 /** 1226 * {@inheritDoc} 1227 */ 1228 @Override 1229 public final boolean equals( final Object o ) 1230 { 1231 var retValue = this == o; 1232 if( !retValue && o instanceof final JavaComposer other ) 1233 { 1234 retValue = (m_Layout == other.m_Layout) && (m_AddDebugOutput == other.m_AddDebugOutput); 1235 } 1236 1237 //---* Done *---------------------------------------------------------- 1238 return retValue; 1239 } // equals() 1240 1241 /** 1242 * Creates a builder for an instance of 1243 * {@link FieldSpec} 1244 * from the given type, name and modifiers. 1245 * 1246 * @param type The type of the {@code FieldSpec} to build. 1247 * @param name The name for the new field. 1248 * @param modifiers The modifiers. 1249 * @return The new builder. 1250 */ 1251 public final FieldSpec.Builder fieldBuilder( final Type type, final CharSequence name, final Modifier... modifiers ) 1252 { 1253 final var retValue = fieldBuilder( TypeNameImpl.from( requireNonNullArgument( type, "type" ) ), name, modifiers ); 1254 1255 //---* Done *---------------------------------------------------------- 1256 return retValue; 1257 } // fieldBuilder() 1258 1259 /** 1260 * Creates a builder for an instance of 1261 * {@link FieldSpec} 1262 * from the given type, name and modifiers. 1263 * 1264 * @param type The type of the {@code FieldSpec} to build. 1265 * @param name The name for the new field. 1266 * @param modifiers The modifiers. 1267 * @return The new builder. 1268 */ 1269 public final FieldSpec.Builder fieldBuilder( final TypeName type, final CharSequence name, final Modifier... modifiers ) 1270 { 1271 final var retValue = new FieldSpecImpl.BuilderImpl( this, (TypeNameImpl) requireNonNullArgument( type, "type" ), require( name, _ -> "not a valid name: %s".formatted( name ), JavaUtils::isValidName ) ).addModifiers( requireNonNullArgument( modifiers, "modifiers" ) ); 1272 1273 //---* Done *---------------------------------------------------------- 1274 return retValue; 1275 } // fieldBuilder() 1276 1277 /** 1278 * Creates a builder for an instance of 1279 * {@link FieldSpec} 1280 * from the given type, name and modifiers. 1281 * 1282 * @param type The type of the {@code FieldSpec} to build. 1283 * @param name The name for the new field. 1284 * @param modifiers The modifiers. 1285 * @return The new builder. 1286 */ 1287 public final FieldSpec.Builder fieldBuilder( final TypeSpec type, final CharSequence name, final Modifier... modifiers ) 1288 { 1289 final var typeName = ClassNameImpl.from( EMPTY_STRING, requireNonNullArgument( type, "type" ).name().orElseThrow( () -> new ValidationException( "Anonymous class cannot be used as type for a field" ) ) ); 1290 final var retValue = fieldBuilder( typeName, name, modifiers ); 1291 1292 //---* Done *---------------------------------------------------------- 1293 return retValue; 1294 } // fieldBuilder() 1295 1296 /** 1297 * Returns the layout that is used to format the output. 1298 * 1299 * @return The layout. 1300 */ 1301 public final Layout getLayout() { return m_Layout; } 1302 1303 /** 1304 * <p>{@summary Returns the maximum number of fields for a class.} If this 1305 * number is exceeded, an annotation 1306 * {@link SuppressWarnings @SuppressWarnings} 1307 * with 1308 * {@link SuppressableWarnings#CLASS_WITH_TOO_MANY_FIELDS} 1309 * will be added to the class.</p> 1310 * 1311 * @return The maximum number of fields. 1312 */ 1313 public final int getMaxFields() { return m_MaxFields; } 1314 1315 /** 1316 * <p>{@summary Returns the maximum number of methods for a class.} If 1317 * this number is exceeded, an annotation 1318 * {@link SuppressWarnings @SuppressWarnings} 1319 * with 1320 * {@link SuppressableWarnings#CLASS_WITH_TOO_MANY_METHODS} 1321 * will be added to the class.</p> 1322 * 1323 * @return The maximum number of fields. 1324 */ 1325 public final int getMaxMethods() { return m_MaxMethods; } 1326 1327 /** 1328 * {@inheritDoc} 1329 */ 1330 @Override 1331 public final int hashCode() { return hash( m_Layout, Boolean.valueOf( m_AddDebugOutput ) ) ; } 1332 1333 /** 1334 * Creates a builder for an interface. 1335 * 1336 * @param className The name of the class. 1337 * @return The builder. 1338 */ 1339 public final TypeSpec.Builder interfaceBuilder( final ClassName className ) 1340 { 1341 final var retValue = interfaceBuilder( requireNonNullArgument( className, "className" ).simpleName() ); 1342 1343 //---* Done *---------------------------------------------------------- 1344 return retValue; 1345 } // interfaceBuilder() 1346 1347 /** 1348 * Creates a builder for an interface. 1349 * 1350 * @param name The name of the class. 1351 * @return The builder. 1352 */ 1353 public final TypeSpec.Builder interfaceBuilder( final CharSequence name ) 1354 { 1355 final var retValue = new InterfaceSpecImpl.BuilderImpl( new JavaComposer(), requireNotEmptyArgument( name, "name" ) ); 1356 1357 //---* Done *---------------------------------------------------------- 1358 return retValue; 1359 } // interfaceBuilder() 1360 1361 /** 1362 * Creates a builder for a new instance of 1363 * {@link JavaFile} 1364 * from the given package name and class definition. 1365 * 1366 * @param packageName The package name; may be empty for the default 1367 * package. 1368 * @param typeSpec The class definition. 1369 * @return The builder. 1370 */ 1371 public final JavaFile.Builder javaFileBuilder( final CharSequence packageName, final TypeSpec typeSpec ) 1372 { 1373 final var retValue = new JavaFileImpl.BuilderImpl( this, packageName, (TypeSpecImpl) typeSpec ); 1374 1375 //---* Done *---------------------------------------------------------- 1376 return retValue; 1377 } // javaFileBuilder() 1378 1379 /** 1380 * Creates a builder for an instance of 1381 * {@link LambdaSpec}. 1382 * 1383 * @return The new builder. 1384 */ 1385 public final LambdaSpec.Builder lambdaBuilder() 1386 { 1387 final var retValue = new LambdaSpecImpl.BuilderImpl( this ); 1388 1389 //---* Done *---------------------------------------------------------- 1390 return retValue; 1391 } // lambdaBuilder() 1392 1393 /** 1394 * Returns a builder for a regular method. 1395 * 1396 * @param name The name for the method. 1397 * @return The builder. 1398 */ 1399 public final MethodSpec.Builder methodBuilder( final CharSequence name ) 1400 { 1401 final var retValue = new MethodSpecImpl.BuilderImpl( new JavaComposer(), name ); 1402 1403 //---* Done *---------------------------------------------------------- 1404 return retValue; 1405 } // methodBuilder() 1406 1407 /** 1408 * <p>{@summary Returns a new method builder for a method that overrides 1409 * the given method.}</p> 1410 * <p>This new builder will copy visibility modifiers, type parameters, 1411 * return type, name, parameters, and {@code throws} declarations. An 1412 * {@link Override} 1413 * annotation will be added.</p> 1414 * 1415 * @note In JavaPoet 1.2 through 1.7 this method retained annotations 1416 * from the method and parameters of the overridden method. Since 1417 * JavaPoet 1.8 and in JavaComposer annotations must be added 1418 * separately. 1419 * 1420 * @param method The method to override. 1421 * @return The builder. 1422 */ 1423 public final MethodSpec.Builder overridingMethodBuilder( final ExecutableElement method ) 1424 { 1425 final var enclosingClass = requireNonNullArgument( method, "method" ).getEnclosingElement(); 1426 if( enclosingClass.getModifiers().contains( FINAL ) ) 1427 { 1428 //noinspection UnnecessaryToStringCall 1429 throw new IllegalArgumentException( format( MSG_CannotOverrideFinalClass, enclosingClass.toString() ) ); 1430 } 1431 1432 var modifiers = method.getModifiers(); 1433 if( modifiers.contains( PRIVATE ) || modifiers.contains( FINAL ) || modifiers.contains( STATIC ) ) 1434 { 1435 throw new IllegalArgumentException( format( MSG_CannotOverrideMethod, modifiers.stream().map( Enum::name ).collect( joining( "', '" ) ) ) ); 1436 } 1437 1438 final var methodName = method.getSimpleName().toString(); 1439 final var retValue = methodBuilder( methodName ); 1440 1441 retValue.addAnnotation( Override.class ); 1442 1443 modifiers = new LinkedHashSet<>( modifiers ); 1444 modifiers.remove( ABSTRACT ); 1445 modifiers.remove( DEFAULT ); 1446 retValue.addModifiers( modifiers ); 1447 1448 for( final var typeParameterElement : method.getTypeParameters() ) 1449 { 1450 final var typeVariable = (TypeVariable) typeParameterElement.asType(); 1451 retValue.addTypeVariable( TypeVariableName.from( typeVariable ) ); 1452 } 1453 1454 retValue.returns( TypeName.from( method.getReturnType() ) ); 1455 1456 retValue.addParameters( parametersOf( method ) ); 1457 retValue.varargs( method.isVarArgs() ); 1458 1459 for( final var thrownType : method.getThrownTypes() ) 1460 { 1461 retValue.addException( TypeName.from( thrownType ) ); 1462 } 1463 1464 //---* Done *---------------------------------------------------------- 1465 return retValue; 1466 } // overridingMethodBuilder() 1467 1468 /** 1469 * <p>{@summary Returns a new method builder that overrides the given 1470 * method as a member of of the given enclosing class.} This will resolve 1471 * type parameters: for example overriding 1472 * {@link Comparable#compareTo} 1473 * in a type that implements {@code Comparable<Movie>}, the {@code T} 1474 * parameter will be resolved to {@code Movie}.</p> 1475 * <p>This will copy its visibility modifiers, type parameters, return 1476 * type, name, parameters, and {@code throws} declarations. An 1477 * {@link Override} 1478 * annotation will be added.</p> 1479 * 1480 * @note In JavaPoet 1.2 through 1.7 this method retained annotations 1481 * from the method and parameters of the overridden method. Since 1482 * JavaPoet 1.8 and in JavaComposer annotations must be added 1483 * separately. 1484 * 1485 * @param method The method to override. 1486 * @param enclosing The enclosing class for the method. 1487 * @param typeUtils An implementation of some utility methods for 1488 * operating on types, as provided by the processing environment of an 1489 * annotation processor. 1490 * @return The builder. 1491 */ 1492 public final MethodSpec.Builder overridingMethodBuilder( final ExecutableElement method, final DeclaredType enclosing, final Types typeUtils ) 1493 { 1494 final var executableType = (ExecutableType) requireNonNullArgument( typeUtils, "types" ).asMemberOf( requireNonNullArgument( enclosing, "enclosing" ), requireNonNullArgument( method, "method" ) ); 1495 final var resolvedParameterTypes = executableType.getParameterTypes(); 1496 final var resolvedThrownTypes = executableType.getThrownTypes(); 1497 final var resolvedReturnType = executableType.getReturnType(); 1498 1499 final var retValue = (MethodSpecImpl.BuilderImpl) overridingMethodBuilder( method ); 1500 retValue.returns( TypeName.from( resolvedReturnType ) ); 1501 final var parameters = retValue.parameters(); 1502 for( int i = 0, size = parameters.size(); i < size; ++i ) 1503 { 1504 final var parameter = parameters.get( i ); 1505 final var type = TypeName.from( resolvedParameterTypes.get( i ) ); 1506 parameters.set( i, parameter.toBuilder( type, parameter.name(), true ).build() ); 1507 } 1508 retValue.exceptions().clear(); 1509 for( final var resolvedThrownType : resolvedThrownTypes ) 1510 { 1511 retValue.addException( TypeName.from( resolvedThrownType ) ); 1512 } 1513 1514 //---* Done *---------------------------------------------------------- 1515 return retValue; 1516 } // overridingMethodBuilder() 1517 1518 /** 1519 * <p>{@summary Returns a new method builder for a method that overrides 1520 * the given method.}</p> 1521 * <p>This new builder will copy visibility modifiers, type parameters, 1522 * return type, name, parameters, and {@code throws} declarations. An 1523 * {@link Override} 1524 * annotation will be added, but any other annotation will be omitted; 1525 * this is consistent with the behaviour of 1526 * {@link #overridingMethodBuilder(ExecutableElement)} 1527 * and 1528 * {@link #overridingMethodBuilder(ExecutableElement, DeclaredType, Types)}.</p> 1529 * 1530 * @param method The method to override. 1531 * @return The builder. 1532 */ 1533 public final MethodSpec.Builder overridingMethodBuilder( final Method method ) 1534 { 1535 final var enclosingClass = requireNonNullArgument( method, "method" ).getDeclaringClass(); 1536 if( translateModifiers( enclosingClass.getModifiers() ).contains( FINAL ) ) 1537 { 1538 throw new IllegalArgumentException( format( MSG_CannotOverrideFinalClass, enclosingClass.getName() ) ); 1539 } 1540 1541 var modifiers = translateModifiers( method.getModifiers() ); 1542 if( modifiers.contains( PRIVATE ) || modifiers.contains( FINAL ) || modifiers.contains( STATIC ) ) 1543 { 1544 throw new IllegalArgumentException( format( MSG_CannotOverrideMethod, modifiers.stream().map( Enum::name ).collect( joining( "', '" ) ) ) ); 1545 } 1546 final var methodName = method.getName(); 1547 final var retValue = methodBuilder( methodName ); 1548 1549 retValue.addAnnotation( Override.class ); 1550 1551 modifiers = new LinkedHashSet<>( modifiers ); 1552 modifiers.remove( ABSTRACT ); 1553 modifiers.remove( DEFAULT ); 1554 retValue.addModifiers( modifiers ); 1555 1556 for( final var typeParameterVariable : method.getTypeParameters() ) 1557 { 1558 retValue.addTypeVariable( TypeVariableName.from( typeParameterVariable ) ); 1559 } 1560 1561 retValue.returns( TypeName.from( method.getReturnType() ) ); 1562 1563 retValue.addParameters( parametersOf( method ) ); 1564 retValue.varargs( method.isVarArgs() ); 1565 1566 for( final var thrownType : method.getExceptionTypes() ) 1567 { 1568 retValue.addException( TypeName.from( thrownType ) ); 1569 } 1570 1571 //---* Done *---------------------------------------------------------- 1572 return retValue; 1573 } // overridingMethodBuilder() 1574 1575 /** 1576 * Creates a builder for a new 1577 * {@link ParameterSpec} 1578 * instance. 1579 * 1580 * @param type The type of the new parameter. 1581 * @param name The name of the new parameter. 1582 * @param modifiers The modifiers for the new parameter. 1583 * @return The builder. 1584 */ 1585 public final ParameterSpec.Builder parameterBuilder( final Type type, final CharSequence name, final Modifier... modifiers ) 1586 { 1587 final var retValue = parameterBuilder( TypeNameImpl.from( requireNonNullArgument( type, "type" ) ), name, modifiers ); 1588 1589 //---* Done *---------------------------------------------------------- 1590 return retValue; 1591 } // parameterBuilder() 1592 1593 /** 1594 * Creates a builder for a new 1595 * {@link ParameterSpec} 1596 * instance. 1597 * 1598 * @param type The type of the new parameter. 1599 * @param name The name of the new parameter. 1600 * @param modifiers The modifiers for the new parameter. 1601 * @return The builder. 1602 */ 1603 public final ParameterSpec.Builder parameterBuilder( final TypeName type, final CharSequence name, final Modifier... modifiers ) 1604 { 1605 final var retValue = new ParameterSpecImpl.BuilderImpl( this, type, name ).addModifiers( modifiers ); 1606 1607 //---* Done *---------------------------------------------------------- 1608 return retValue; 1609 } // parameterBuilder() 1610 1611 /** 1612 * Creates a new 1613 * {@link ParameterSpec} 1614 * instance for the given arguments. 1615 * 1616 * @param type The type of the new parameter. 1617 * @param name The name of the new parameter. 1618 * @param modifiers The modifiers for the new parameter. 1619 * @return The parameter specification. 1620 */ 1621 public final ParameterSpec parameterOf( final Type type, final CharSequence name, final Modifier... modifiers ) 1622 { 1623 final var retValue = parameterOf( TypeNameImpl.from( requireNonNullArgument( type, "type" ) ), name, modifiers ); 1624 1625 //---* Done *---------------------------------------------------------- 1626 return retValue; 1627 } // parameterOf() 1628 1629 /** 1630 * Creates a new 1631 * {@link ParameterSpec} 1632 * instance for the given arguments. 1633 * 1634 * @param type The type of the new parameter. 1635 * @param name The name of the new parameter. 1636 * @param modifiers The modifiers for the new parameter. 1637 * @return The parameter specification. 1638 */ 1639 public final ParameterSpec parameterOf( final TypeName type, final CharSequence name, final Modifier... modifiers ) 1640 { 1641 final var retValue = parameterBuilder( type, name, modifiers ) 1642 .build(); 1643 1644 //---* Done *---------------------------------------------------------- 1645 return retValue; 1646 } // parameterOf() 1647 1648 /** 1649 * Retrieves the parameters from the given method. 1650 * 1651 * @param method The method. 1652 * @return The parameters of the given method; the returned list can be 1653 * empty, but it will not be {@code null}. 1654 */ 1655 public final List<ParameterSpec> parametersOf( final ExecutableElement method ) 1656 { 1657 final var retValue = requireNonNullArgument( method, "method" ).getParameters() 1658 .stream() 1659 .map( this::createParameter ) 1660 .toList(); 1661 1662 //---* Done *---------------------------------------------------------- 1663 return retValue; 1664 } // parametersOf() 1665 1666 /** 1667 * Retrieves the parameters from the given method. 1668 * 1669 * @param method The method. 1670 * @return The parameters of the given method; the returned list can be 1671 * empty, but it will not be {@code null}. 1672 */ 1673 public final List<ParameterSpec> parametersOf( final Method method ) 1674 { 1675 final var retValue = stream( requireNonNullArgument( method, "method" ).getParameters() ) 1676 .map( this::createParameter ) 1677 .toList(); 1678 1679 //---* Done *---------------------------------------------------------- 1680 return retValue; 1681 } // parametersOf() 1682 1683 /** 1684 * Creates a builder for a record. 1685 * 1686 * @param className The name of the record type. 1687 * @return The builder. 1688 */ 1689 public final TypeSpec.Builder recordBuilder( final ClassName className ) 1690 { 1691 final var retValue = recordBuilder( requireNonNullArgument( className, "className" ).simpleName() ); 1692 1693 //---* Done *---------------------------------------------------------- 1694 return retValue; 1695 } // recordBuilder() 1696 1697 /** 1698 * Creates a builder for a record. 1699 * 1700 * @param name The name of the record type. 1701 * @return The builder. 1702 */ 1703 public final TypeSpec.Builder recordBuilder( final CharSequence name ) 1704 { 1705 final var retValue = new RecordSpecImpl.BuilderImpl( this, requireNotEmptyArgument( name, "name" ) ); 1706 1707 //---* Done *---------------------------------------------------------- 1708 return retValue; 1709 } // recordBuilder() 1710 1711 /** 1712 * <p>{@summary Sets the maximum number of fields for a class.} If this 1713 * number is exceeded, an annotation 1714 * {@link SuppressWarnings @SuppressWarnings} 1715 * with 1716 * {@link SuppressableWarnings#CLASS_WITH_TOO_MANY_FIELDS} 1717 * will be added to the class.</p> 1718 * <p>Setting the value to 0 or a negative value will disable this 1719 * feature.</p> 1720 * 1721 * @param value The value. 1722 */ 1723 public final void setMaxFields( final int value ) { m_MaxFields = value; } 1724 1725 /** 1726 * <p>{@summary Sets the maximum number of methods for a class.} If this 1727 * number is exceeded, an annotation 1728 * {@link SuppressWarnings @SuppressWarnings} 1729 * with 1730 * {@link SuppressableWarnings#CLASS_WITH_TOO_MANY_METHODS} 1731 * will be added to the class.</p> 1732 * <p>Setting the value to 0 or a negative value will disable this 1733 * feature.</p> 1734 * 1735 * @param value The value. 1736 */ 1737 public final void setMaxMethods( final int value ) { m_MaxMethods = value; } 1738 1739 /** 1740 * Creates a new 1741 * {@link CodeBlock} 1742 * instance from the given format and arguments, using 1743 * {@link CodeBlock.Builder#addStatement(String, Object...)}. 1744 * 1745 * @note No debug info will be added. 1746 * 1747 * @param format The format. 1748 * @param args The arguments. 1749 * @return The new code block. 1750 */ 1751 public final CodeBlock statementOf( final String format, final Object... args ) 1752 { 1753 final var retValue = ((CodeBlockImpl.BuilderImpl) codeBlockBuilder()) 1754 .addStatement( format, args ) 1755 .build(); 1756 1757 //---* Done *---------------------------------------------------------- 1758 return retValue; 1759 } // codeBlockOf() 1760} 1761// class JavaComposer 1762 1763/* 1764 * End of File 1765 */