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.config.ap; 019 020import static java.lang.String.format; 021import static org.apiguardian.api.API.Status.MAINTAINED; 022import static org.tquadrat.foundation.config.ap.CollectionKind.NO_COLLECTION; 023import static org.tquadrat.foundation.util.StringUtils.capitalize; 024 025import javax.lang.model.element.Name; 026import java.util.List; 027import java.util.Optional; 028import java.util.OptionalInt; 029import java.util.Set; 030 031import org.apiguardian.api.API; 032import org.tquadrat.foundation.annotation.ClassVersion; 033import org.tquadrat.foundation.config.SpecialPropertyType; 034import org.tquadrat.foundation.config.ap.impl.CodeBuilder; 035import org.tquadrat.foundation.exception.UnsupportedEnumError; 036import org.tquadrat.foundation.javacomposer.CodeBlock; 037import org.tquadrat.foundation.javacomposer.FieldSpec; 038import org.tquadrat.foundation.javacomposer.MethodSpec; 039import org.tquadrat.foundation.javacomposer.ParameterizedTypeName; 040import org.tquadrat.foundation.javacomposer.TypeName; 041 042/** 043 * The specification for a property of a configuration bean. 044 * 045 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 046 * @version $Id: PropertySpec.java 1105 2024-02-28 12:58:46Z tquadrat $ 047 * @since 0.1.0 048 * 049 * @UMLGraph.link 050 */ 051@SuppressWarnings( "ClassWithTooManyMethods" ) 052@ClassVersion( sourceVersion = "$Id: PropertySpec.java 1105 2024-02-28 12:58:46Z tquadrat $" ) 053@API( status = MAINTAINED, since = "0.1.0" ) 054public interface PropertySpec 055{ 056 /*---------------*\ 057 ====** Inner Classes **==================================================== 058 \*---------------*/ 059 /** 060 * The flags for a property. 061 * 062 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 063 * @version $Id: PropertySpec.java 1105 2024-02-28 12:58:46Z tquadrat $ 064 * @since 0.1.0 065 * 066 * @UMLGraph.link 067 */ 068 @ClassVersion( sourceVersion = "$Id: PropertySpec.java 1105 2024-02-28 12:58:46Z tquadrat $" ) 069 @API( status = MAINTAINED, since = "0.1.0" ) 070 public static enum PropertyFlag 071 { 072 /*------------------*\ 073 ====** Enum Declaration **============================================= 074 \*------------------*/ 075 /** 076 * Indicates that this property can be initialised from and persisted 077 * to an 078 * {@link org.tquadrat.foundation.inifile.INIFile}. 079 * This also means that the methods 080 * {@link #getINIGroup()} 081 * and 082 * {@link #getINIKey()} 083 * may not return 084 * {@linkplain Optional#empty() empty}. 085 */ 086 ALLOWS_INIFILE, 087 088 /** 089 * Indicates that this property can be initialised from and persisted 090 * to a preference. This also means that the methods 091 * {@link #getPrefsAccessorClass()} 092 * and 093 * {@link #getPrefsKey()} 094 * may not return 095 * {@linkplain Optional#empty() empty}. 096 */ 097 ALLOWS_PREFERENCES, 098 099 /** 100 * <p>{@summary Indicates that the element type of a collection is an 101 * enum type.} Obviously this is only relevant if the property is a 102 * collection.</p> 103 * 104 * @see PropertySpec#getCollectionKind() 105 * @see CollectionKind#SET 106 * @see CollectionKind#LIST 107 */ 108 ELEMENTTYPE_IS_ENUM, 109 110 /** 111 * Indicates that this property is initialised from the value of an 112 * environment variable. The method 113 * {@link #getEnvironmentVariableName()} 114 * may not return 115 * {@linkplain Optional#empty() empty}. 116 * 117 * @see System#getenv() 118 */ 119 ENVIRONMENT_VARIABLE, 120 121 /** 122 * <p>{@summary Indicates that this property will not be part of the 123 * map, in case the configuration bean implements 124 * {@link java.util.Map}.}</p> 125 * <p>There is no related annotation for this flag, it is used mainly 126 * for 127 * {@linkplain org.tquadrat.foundation.config.SpecialProperty special properties}.</p> 128 */ 129 EXEMPT_FROM_MAP, 130 131 /** 132 * Indicates that this property will not appear in the return value of 133 * {@link #toString()}. 134 */ 135 @SuppressWarnings( "SpellCheckingInspection" ) 136 EXEMPT_FROM_TOSTRING, 137 138 /** 139 * <p>{@summary Indicates that the getter method for this property is 140 * default.} This means that the method will not be implemented, and 141 * that there is no field for the property.</p> 142 * <p>This flag implies 143 * {@link #EXEMPT_FROM_MAP}.</p> 144 */ 145 GETTER_IS_DEFAULT, 146 147 /** 148 * <p>{@summary Indicates that the implementation of 149 * {@link java.util.Map} 150 * will refer to the getter for this property, instead of the 151 * field.}</p> 152 * <p>There is no related annotation for this flag, it is used mainly 153 * for 154 * {@linkplain org.tquadrat.foundation.config.SpecialProperty special properties}.</p> 155 */ 156 GETTER_ON_MAP, 157 158 /** 159 * Indicates that the getter method returns an instance of 160 * {@link Optional} 161 * holding the configuration value. 162 */ 163 GETTER_RETURNS_OPTIONAL, 164 165 /** 166 * Indicates that a value for this property is required on the 167 * command line. 168 */ 169 PROPERTY_CLI_MANDATORY, 170 171 /** 172 * Indicates that it is possible to provide multiple values for this 173 * property on the command line. 174 */ 175 PROPERTY_CLI_MULTIVALUED, 176 177 /** 178 * Indicates that the property can be initialised with a command line 179 * argument. 180 * 181 * @see #getCLIArgumentIndex() 182 * @see #getCLIValueHandlerClass() 183 * @see #getCLIFormat() 184 * @see #getCLIMetaVar() 185 */ 186 PROPERTY_IS_ARGUMENT, 187 188 /** 189 * Indicates that the property value can be modified. Basically, it 190 * means that there is a setter and/or an 'add' method for that 191 * property. 192 */ 193 PROPERTY_IS_MUTABLE, 194 195 /** 196 * Indicates that the property can be initialised with a command line 197 * option. 198 * 199 * @see #getCLIOptionNames() 200 * @see #getCLIValueHandlerClass() 201 * @see #getCLIFormat() 202 * @see #getCLIMetaVar() 203 */ 204 PROPERTY_IS_OPTION, 205 206 /** 207 * Indicates that the property is a 'special' property. This also 208 * means that 209 * {@link PropertySpec#getSpecialPropertyType()} 210 * will not return an 211 * {@linkplain Optional#empty() empty} 212 * value. 213 */ 214 PROPERTY_IS_SPECIAL, 215 216 /** 217 * Indicates that the access to the property requires synchronisation. 218 */ 219 PROPERTY_REQUIRES_SYNCHRONIZATION, 220 221 /** 222 * Indicates that the setter method for this property should check for 223 * an empty argument. 224 */ 225 SETTER_CHECK_EMPTY, 226 227 /** 228 * Indicates that the setter method for this property should check for 229 * a null argument. 230 */ 231 SETTER_CHECK_NULL, 232 233 /** 234 * Indicates that the setter method for this property is default; this 235 * means that the method will not be implemented, and that there is no 236 * field for the property. 237 */ 238 SETTER_IS_DEFAULT, 239 240 /** 241 * Indicates that this property is initialised from the value of a 242 * system preference. The methods 243 * {@link #getSystemPrefsPath()}, 244 * {@link #getPrefsKey()} 245 * and 246 * {@link #getPrefsAccessorClass()} 247 * may not return 248 * {@linkplain Optional#empty() empty}. 249 */ 250 SYSTEM_PREFERENCE, 251 252 /** 253 * Indicates that this property is initialised from the value of a 254 * system property. The method 255 * {@link #getSystemPropertyName()} 256 * may not return 257 * {@linkplain Optional#empty() empty}. 258 * 259 * @see System#getProperties() 260 */ 261 SYSTEM_PROPERTY 262 } 263 // enum PropertyFlag 264 265 /*-----------*\ 266 ====** Constants **======================================================== 267 \*-----------*/ 268 269 /*---------*\ 270 ====** Methods **========================================================== 271 \*---------*/ 272 /** 273 * Creates the specification of the 'add' method for this property. 274 * 275 * @param codeBuilder The factory for the code generation. 276 * @return An instance of 277 * {@link Optional} 278 * that holds the method specification. 279 */ 280 public Optional<MethodSpec> createAddMethod( final CodeBuilder codeBuilder ); 281 282 /** 283 * Creates a code block that is a fragment for the constructor of the new 284 * configuration bean and that initialises this property. 285 * 286 * @param codeBuilder The factory for the code generation. 287 * @return An instance of 288 * {@link Optional} 289 * that holds the code block. 290 */ 291 public Optional<CodeBlock> createConstructorFragment( final CodeBuilder codeBuilder ); 292 293 /** 294 * Creates the field specification for this property. 295 * 296 * @param codeBuilder The factory for the code generation. 297 * @return An instance of 298 * {@link Optional} 299 * that holds the field specification. 300 */ 301 public Optional<FieldSpec> createField( final CodeBuilder codeBuilder ); 302 303 /** 304 * Creates the specification of the getter for this property. 305 * 306 * @param codeBuilder The factory for the code generation. 307 * @return An instance of 308 * {@link Optional} 309 * that holds the method specification. 310 */ 311 public Optional<MethodSpec> createGetter( final CodeBuilder codeBuilder ); 312 313 /** 314 * Creates the specification of the setter for this property. 315 * 316 * @param codeBuilder The factory for the code generation. 317 * @return An instance of 318 * {@link Optional} 319 * that holds the method specification. 320 */ 321 public Optional<MethodSpec> createSetter( final CodeBuilder codeBuilder ); 322 323 /** 324 * Returns the name of the 'add' method's argument. 325 * 326 * @return The argument name; is probable {@code null} when 327 * {@link #getAddMethodName()} 328 * returns 329 * {@link Optional#empty()}. 330 */ 331 public Name getAddMethodArgumentName(); 332 333 /** 334 * Returns the name of the 'add' method for this property. 335 * 336 * @return An instance of 337 * {@link Optional} 338 * that holds the name of the add method. 339 */ 340 public Optional<Name> getAddMethodName(); 341 342 /** 343 * <p>{@summary Returns the index for an argument on the command 344 * line.}</p> 345 * <p>The return value will be 346 * {@linkplain OptionalInt#empty() empty} 347 * if the property is not a CLI argument.</p> 348 * 349 * @return An instance of 350 * {@link OptionalInt} 351 * that holds the index. 352 * 353 * @see org.tquadrat.foundation.config.Argument#index() 354 */ 355 public OptionalInt getCLIArgumentIndex(); 356 357 /** 358 * Returns the special CLI format. 359 * 360 * @return An instance of 361 * {@link Optional} 362 * that holds the format. 363 * 364 * @see org.tquadrat.foundation.config.Argument#format() 365 * @see org.tquadrat.foundation.config.Option#format() 366 */ 367 public Optional<String> getCLIFormat(); 368 369 /** 370 * Returns the name of the CLI meta variable for this property. 371 * 372 * @return An instance of 373 * {@link Optional} 374 * that holds the name of the meta variable. 375 * 376 * @see org.tquadrat.foundation.config.Argument#metaVar() 377 * @see org.tquadrat.foundation.config.Option#metaVar() 378 */ 379 public Optional<String> getCLIMetaVar(); 380 381 /** 382 * <p>{@summary Returns the CLI option names.} The mandatory first name in 383 * the list is the primary name, the optional others are the aliases.</p> 384 * <p>The return value will be 385 * {@linkplain Optional#empty() empty} 386 * if the property is not a CLI option.</p> 387 * 388 * @return An instance of 389 * {@link Optional} 390 * that holds the option names. 391 * 392 * @see org.tquadrat.foundation.config.Option#name() 393 * @see org.tquadrat.foundation.config.Option#aliases() 394 */ 395 @SuppressWarnings( "OptionalContainsCollection" ) 396 public Optional<List<String>> getCLIOptionNames(); 397 398 /** 399 * Returns the CLI usage text for this property. This text will not be 400 * localised. 401 * 402 * @return An instance of 403 * {@link Optional} 404 * that holds the usage text. 405 * 406 * @see org.tquadrat.foundation.config.Argument#usage() 407 * @see org.tquadrat.foundation.config.Option#usage() 408 */ 409 public Optional<String> getCLIUsage(); 410 411 /** 412 * Returns the CLI usage key for this property. This key is used to 413 * retrieve a localised usage text. 414 * 415 * @return An instance of 416 * {@link Optional} 417 * that holds the usage key. 418 * 419 * @see org.tquadrat.foundation.config.Argument#usageKey() 420 * @see org.tquadrat.foundation.config.Option#usageKey() 421 */ 422 public Optional<String> getCLIUsageKey(); 423 424 /** 425 * Returns the CLI value handler class for this property. 426 * 427 * @return An instance of 428 * {@link Optional} 429 * that holds the 430 * {@link TypeName} 431 * for the handler class. 432 * 433 * @see org.tquadrat.foundation.config.Argument#handler() 434 * @see org.tquadrat.foundation.config.Option#handler() 435 */ 436 public Optional<TypeName> getCLIValueHandlerClass(); 437 438 /** 439 * Returns the kind of collection for this property. 440 * 441 * @return The collection kind. 442 */ 443 public CollectionKind getCollectionKind(); 444 445 /** 446 * If the property is a collection (either 447 * {@link Set} 448 * or 449 * {@link List}, 450 * this method returns the element type of that collection. 451 * 452 * @return An instance of 453 * {@link Optional} 454 * that holds the element type. 455 */ 456 public default Optional<TypeName> getElementType() 457 { 458 final var elementType = switch( getCollectionKind() ) 459 { 460 case LIST, SET -> 461 { 462 if( getPropertyType() instanceof final ParameterizedTypeName propertyType ) 463 { 464 final var typeArguments = propertyType.typeArguments(); 465 yield typeArguments.getFirst(); 466 } 467 yield null; 468 } 469 470 case MAP, NO_COLLECTION -> null; 471 472 default -> throw new UnsupportedEnumError( getCollectionKind() ); 473 }; 474 475 final var retValue = Optional.ofNullable( elementType ); 476 477 //---* Done *---------------------------------------------------------- 478 return retValue; 479 } // getElementType() 480 481 /** 482 * <p>{@summary Returns the default value for an environment variable or a 483 * system property.} This is used to initialise this property when it has 484 * the annotation 485 * {@link org.tquadrat.foundation.config.EnvironmentVariable @EnvironmentVariable} 486 * or 487 * {@link org.tquadrat.foundation.config.SystemProperty @EnvironmentVariable}, 488 * but no value is provided.</p> 489 * <p>A default value is mandatory when the annotated property has a 490 * primitive type.</p> 491 * 492 * @return An instance of 493 * {@link Optional} 494 * that holds the default value. 495 */ 496 public Optional<String> getEnvironmentDefaultValue(); 497 498 /** 499 * Returns the name of the environment variable that is used to initialise 500 * this property. 501 * 502 * @return An instance of 503 * {@link Optional} 504 * that holds the environment variable name. 505 * 506 * @see org.tquadrat.foundation.config.EnvironmentVariable 507 */ 508 public Optional<String> getEnvironmentVariableName(); 509 510 /** 511 * Returns the name of the field for the property. 512 * 513 * @return The field name. 514 */ 515 public default String getFieldName() { return format( "m_%s", capitalize( getPropertyName() ) ); } 516 517 /** 518 * Returns a builder for the getter for this property. 519 * 520 * @return An instance of 521 * {@link Optional} 522 * that holds the builder. 523 */ 524 public Optional<MethodSpec.Builder> getGetterBuilder(); 525 526 /** 527 * Returns the name of the getter method name. If there is no name for the 528 * method, it will not be generated. 529 * 530 * @return An instance of 531 * {@link Optional} 532 * that holds the name of the getter method. 533 */ 534 public Optional<Name> getGetterMethodName(); 535 536 /** 537 * Returns the return type of the getter. This is not necessarily the same 538 * as the 539 * {@linkplain #getPropertyType() property type}. 540 * 541 * @return The getter's return type. 542 */ 543 public TypeName getGetterReturnType(); 544 545 /** 546 * Returns the comment for this property in the {@code INI} file. 547 * 548 * @return An instance of 549 * {@link Optional} 550 * that holds the comment. 551 */ 552 public Optional<String> getINIComment(); 553 554 /** 555 * Returns the group for this property in the {@code INI} file. 556 * 557 * @return An instance of 558 * {@link Optional} 559 * that holds the group name. 560 */ 561 public Optional<String> getINIGroup(); 562 563 /** 564 * Returns the key for this property in the {@code INI} file. 565 * 566 * @return An instance of 567 * {@link Optional} 568 * that holds the key. 569 */ 570 public Optional<String> getINIKey(); 571 572 /** 573 * <p>{@summary Returns the {@code Preferences} accessor class.}</p> 574 * <p>This is used when this property is linked to a preference, but also 575 * to initialise it from a SYSTEM preference.</p> 576 * 577 * @see org.tquadrat.foundation.config.PreferencesBeanSpec 578 * @see org.tquadrat.foundation.config.Preference#accessor() 579 * @see org.tquadrat.foundation.config.SystemPreference#accessor() 580 * 581 * @return An instance of 582 * {@link Optional} 583 * that holds the 584 * {@link TypeName} 585 * for the accessor class. 586 */ 587 public Optional<TypeName> getPrefsAccessorClass(); 588 589 /** 590 * <p>{@summary Returns the {@code Preferences} key for this 591 * property.}</p> 592 * <p>This is used when this property is linked to a preference, but also 593 * to initialise it from a SYSTEM preference. In first case, the name is 594 * defaulted to the property name, while it is mandatory otherwise.</p> 595 * 596 * @see org.tquadrat.foundation.config.PreferencesBeanSpec 597 * @see org.tquadrat.foundation.config.Preference#key() 598 * @see org.tquadrat.foundation.config.SystemPreference#key() 599 * 600 * @return An instance of 601 * {@link Optional} 602 * that holds the preferences key for this property. 603 */ 604 public Optional<String> getPrefsKey(); 605 606 /** 607 * Returns the name of the configuration property. 608 * 609 * @return The name. 610 */ 611 public String getPropertyName(); 612 613 /** 614 * Returns the property type. 615 * 616 * @return The property type. 617 */ 618 public TypeName getPropertyType(); 619 620 /** 621 * Returns the name of the setter's argument. 622 * 623 * @return The argument name; is probably {@code null} when 624 * {@link #getSetterMethodName()} 625 * returns 626 * {@link Optional#empty()}. 627 */ 628 public Name getSetterArgumentName(); 629 630 /** 631 * Returns a builder for the setter for this property. 632 * 633 * @return An instance of 634 * {@link Optional} 635 * that holds the builder. 636 */ 637 public Optional<MethodSpec.Builder> getSetterBuilder(); 638 639 /** 640 * Returns the name of the setter method name. If there is no name for the 641 * method, it will not be generated. 642 * 643 * @return An instance of 644 * {@link Optional} 645 * that holds the name of the setter method. 646 */ 647 public Optional<Name> getSetterMethodName(); 648 649 /** 650 * Return the 'speciality' type for this property. 651 * 652 * @return An instance of 653 * {@link Optional} 654 * that holds the speciality type. 655 */ 656 public Optional<SpecialPropertyType> getSpecialPropertyType(); 657 658 /** 659 * Returns the class that implements the String converter for the type of 660 * this property. 661 * 662 * @return An instance of 663 * {@link Optional} 664 * that holds the implementation class for 665 * {@link org.tquadrat.foundation.lang.StringConverter}. 666 */ 667 public Optional<TypeName> getStringConverterClass(); 668 669 /** 670 * Returns the path to the SYSTEM {@code Preferences} node that holds the 671 * data for the initialisation of this property. 672 * 673 * @return An instance of 674 * {@link Optional} 675 * that holds the path. 676 * 677 * @see org.tquadrat.foundation.config.SystemPreference#path() 678 */ 679 public Optional<String> getSystemPrefsPath(); 680 681 /** 682 * Returns the name of the system property that is used to initialise this 683 * property. 684 * 685 * @return An instance of 686 * {@link Optional} 687 * that holds the system property name. 688 * 689 * @see org.tquadrat.foundation.config.SystemProperty 690 */ 691 public Optional<String> getSystemPropertyName(); 692 693 /** 694 * Checks whether the given flag is set for this property. 695 * 696 * @param flag The flag to test for. 697 * @return {@code true} if the flag is set, {@code false} otherwise. 698 */ 699 public boolean hasFlag( final PropertyFlag flag ); 700 701 /** 702 * Checks whether this property is a collection of some kind. 703 * 704 * @return {@code true} if this property is a collection, {@code false} 705 * otherwise. 706 */ 707 public default boolean isCollection() { return getCollectionKind() != NO_COLLECTION; } 708 709 /** 710 * Returns the flag that indicates whether the property is an 711 * {@link Enum enum} 712 * type. 713 * 714 * @return {@code true} if the property type is an {@code enum}, 715 * {@code false} otherwise. 716 */ 717 public boolean isEnum(); 718 719 /** 720 * {@summary Checks whether this property is exposed to the CLI.} This 721 * means that it has either the flag 722 * {@link PropertyFlag#PROPERTY_IS_ARGUMENT} 723 * or the 724 * {@link PropertyFlag#PROPERTY_IS_OPTION} 725 * set to it. 726 * 727 * @return {@code true} if the property is exposed to the CLI, 728 * {@code false} otherwise. 729 */ 730 public default boolean isOnCLI() { return hasFlag( PropertyFlag.PROPERTY_IS_ARGUMENT ) || hasFlag( PropertyFlag.PROPERTY_IS_OPTION ); } 731 732 /** 733 * <p>{@summary 'Merges' the attributes from a 734 * {@linkplain SpecialPropertyType special property} 735 * with the attributes retrieved from the configuration bean 736 * specification and returns a new instance of {@code PropertySpec}.} The 737 * original instance remains unchanged.</p> 738 * <p>If the property is not a special property (the flag 739 * {@link PropertyFlag#PROPERTY_IS_SPECIAL} 740 * is not set), this instance will be returned.</p> 741 * 742 * @return The effective property specification. 743 */ 744 public PropertySpec merge(); 745} 746// interface PropertySpec 747 748/* 749 * End of File 750 */