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