001/* 002 * ============================================================================ 003 * Copyright © 2002-2023 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.impl.codebuilders; 019 020import static java.lang.String.format; 021import static java.util.stream.Collectors.joining; 022import static javax.lang.model.element.Modifier.FINAL; 023import static javax.lang.model.element.Modifier.PRIVATE; 024import static javax.lang.model.element.Modifier.PUBLIC; 025import static org.apiguardian.api.API.Status.MAINTAINED; 026import static org.tquadrat.foundation.config.ap.ConfigAnnotationProcessor.MSG_DuplicateOptionName; 027import static org.tquadrat.foundation.config.ap.ConfigAnnotationProcessor.MSG_InvalidCLIType; 028import static org.tquadrat.foundation.config.ap.ConfigAnnotationProcessor.MSG_NoArgumentIndex; 029import static org.tquadrat.foundation.config.ap.ConfigAnnotationProcessor.MSG_NoOptionName; 030import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.ELEMENTTYPE_IS_ENUM; 031import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.PROPERTY_CLI_MANDATORY; 032import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.PROPERTY_CLI_MULTIVALUED; 033import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.PROPERTY_IS_ARGUMENT; 034import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.PROPERTY_IS_OPTION; 035import static org.tquadrat.foundation.config.ap.impl.CodeBuilder.StandardField.STD_FIELD_CLIDefinitions; 036import static org.tquadrat.foundation.config.ap.impl.CodeBuilder.StandardField.STD_FIELD_CLIError; 037import static org.tquadrat.foundation.config.ap.impl.CodeBuilder.StandardField.STD_FIELD_WriteLock; 038import static org.tquadrat.foundation.config.ap.impl.CodeBuilder.StandardMethod.STD_METHOD_GetRessourceBundle; 039import static org.tquadrat.foundation.javacomposer.Primitives.BOOLEAN; 040import static org.tquadrat.foundation.javacomposer.Primitives.VOID; 041import static org.tquadrat.foundation.javacomposer.SuppressableWarnings.REDUNDANT_EXPLICIT_VARIABLE_TYPE; 042import static org.tquadrat.foundation.javacomposer.SuppressableWarnings.createSuppressWarningsAnnotation; 043import static org.tquadrat.foundation.lang.DebugOutput.ifDebug; 044import static org.tquadrat.foundation.lang.Objects.nonNull; 045import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument; 046import static org.tquadrat.foundation.util.StringUtils.capitalize; 047 048import java.io.IOException; 049import java.io.OutputStream; 050import java.util.ArrayList; 051import java.util.Collection; 052import java.util.HashMap; 053import java.util.HashSet; 054import java.util.List; 055import java.util.Map; 056import java.util.Optional; 057import java.util.function.BiConsumer; 058 059import org.apiguardian.api.API; 060import org.tquadrat.foundation.annotation.ClassVersion; 061import org.tquadrat.foundation.ap.IllegalAnnotationError; 062import org.tquadrat.foundation.config.CmdLineException; 063import org.tquadrat.foundation.config.ConfigUtil; 064import org.tquadrat.foundation.config.ap.CollectionKind; 065import org.tquadrat.foundation.config.ap.PropertySpec; 066import org.tquadrat.foundation.config.cli.CmdLineValueHandler; 067import org.tquadrat.foundation.config.cli.SimpleCmdLineValueHandler; 068import org.tquadrat.foundation.config.internal.ClassRegistry; 069import org.tquadrat.foundation.config.spi.CLIArgumentDefinition; 070import org.tquadrat.foundation.config.spi.CLIDefinition; 071import org.tquadrat.foundation.config.spi.CLIOptionDefinition; 072import org.tquadrat.foundation.javacomposer.ArrayTypeName; 073import org.tquadrat.foundation.javacomposer.ClassName; 074import org.tquadrat.foundation.javacomposer.FieldSpec; 075import org.tquadrat.foundation.javacomposer.LambdaSpec; 076import org.tquadrat.foundation.javacomposer.ParameterizedTypeName; 077import org.tquadrat.foundation.javacomposer.TypeName; 078import org.tquadrat.foundation.javacomposer.WildcardTypeName; 079import org.tquadrat.foundation.lang.Objects; 080import org.tquadrat.foundation.util.stringconverter.EnumStringConverter; 081 082/** 083 * The 084 * {@linkplain org.tquadrat.foundation.config.ap.impl.CodeBuilder code builder implementation} 085 * for the CLI stuff, as defined in 086 * {@link org.tquadrat.foundation.config.CLIBeanSpec}. 087 * 088 * @extauthor Thomas Thrien - thomas.thrien@tquadrat.org 089 * @version $Id: CLIBeanBuilder.java 1231 2026-05-05 14:28:23Z tquadrat $ 090 * @UMLGraph.link 091 * @since 0.1.0 092 */ 093@SuppressWarnings( "OverlyCoupledClass" ) 094@ClassVersion( sourceVersion = "$Id: CLIBeanBuilder.java 1231 2026-05-05 14:28:23Z tquadrat $" ) 095@API( status = MAINTAINED, since = "0.1.0" ) 096public final class CLIBeanBuilder extends CodeBuilderBase 097{ 098 /*------------*\ 099 ====** Attributes **======================================================= 100 \*------------*/ 101 /** 102 * The handler classes. 103 */ 104 private final Map<TypeName,ClassName> m_HandlerClasses = new HashMap<>(); 105 106 /*--------------*\ 107 ====** Constructors **===================================================== 108 \*--------------*/ 109 /** 110 * Creates a new instance of {@code CLIBeanBuilder}. 111 * 112 * @param context The code generator context. 113 */ 114 public CLIBeanBuilder( final CodeGeneratorContext context ) 115 { 116 super( context ); 117 118 for( final var entry : ClassRegistry.m_HandlerClasses.entrySet() ) 119 { 120 m_HandlerClasses.put( TypeName.from( entry.getKey() ), ClassName.from( entry.getValue() ) ); 121 } 122 } // CLIBeanBuilder() 123 124 /*---------*\ 125 ====** Methods **========================================================== 126 \*---------*/ 127 /** 128 * {@inheritDoc} 129 * <p>This method checks whether there are any properties that are either 130 * options or arguments, and does the build only when there is at least 131 * one.</p> 132 * <p>Not building CLI stuff will let crash the compilation of the 133 * generated code, but this is intended: either the annotation for the 134 * CLI properties is missing, or the interface 135 * {@link org.tquadrat.foundation.config.CLIBeanSpec} 136 * was added to the configuration bean specification in error.</p> 137 */ 138 @Override 139 public final void build() 140 { 141 var doBuild = false; 142 for( final var iterator = getProperties(); iterator.hasNext() && !doBuild; ) 143 { 144 final var property = iterator.next(); 145 doBuild = property.hasFlag( PROPERTY_IS_OPTION ) || property.hasFlag( PROPERTY_IS_ARGUMENT ); 146 } 147 if( doBuild ) doBuild(); 148 } // build() 149 150 /** 151 * Composes the code that creates the CLI value handler for the given 152 * property. 153 * 154 * @param property The property. 155 * @return The name of the method that creates the CLI value handler for 156 * this property. 157 */ 158 @SuppressWarnings( {"OverlyCoupledMethod", "OverlyComplexMethod"} ) 159 private final String composeValueHandlerCreation( final PropertySpec property ) 160 { 161 //---* The method name *----------------------------------------------- 162 final var retValue = format( "composeValueHandler_%s", capitalize( property.getPropertyName() ) ); 163 164 final var objectType = WildcardTypeName.subtypeOf( Object.class ); 165 final var handlerType = ParameterizedTypeName.from( ClassName.from( CmdLineValueHandler.class ), objectType ); 166 final var builder = getComposer().codeBlockBuilder(); 167 168 final ParameterizedTypeName lambdaType; 169 final LambdaSpec lambda; 170 171 if( property.isCollection() ) 172 { 173 if( property.getCollectionKind() == CollectionKind.MAP ) 174 { 175 throw new IllegalAnnotationError( "Property '%s' is a map of type '%s'; maps are currently not supported for CLI properties".formatted( property.getPropertyName(), property.getPropertyType().toString() ) ); 176 } 177 178 //---* Determine the element type of the collection *-------------- 179 final var elementType = property.getElementType() 180 .orElseThrow( () -> new IllegalAnnotationError( "Cannot determine element type for property '%s'".formatted( property.getPropertyName() ) ) ); 181 182 //---* The lambda that adds the value to the attribute *----------- 183 lambdaType = ParameterizedTypeName.from( ClassName.from( BiConsumer.class ), ClassName.from( String.class ), elementType ); 184 lambda = getComposer().lambdaBuilder() 185 .addParameter( "propertyName" ) 186 .addParameter( "value" ) 187 .addCode( "$N.add( value )", property.getFieldName() ) 188 .build(); 189 190 //---* Retrieve the class for the value handler *------------------ 191 final var valueHandlerClass = retrieveValueHandlerClass( property ); 192 if( valueHandlerClass.isPresent() ) 193 { 194 builder.addStatement( "final $T retValue = new $T( lambda ) ", handlerType, valueHandlerClass.get() ); 195 } 196 else 197 { 198 //---* Get the StringConverter for the element type *-------------- 199 final var stringConverter = property.getStringConverterClass() 200 .or( () -> getStringConverter( elementType ) ) 201 .or( () -> Optional.ofNullable( property.hasFlag( ELEMENTTYPE_IS_ENUM ) ? ClassName.from( EnumStringConverter.class ) : null ) ) 202 .orElseThrow( () -> new IllegalAnnotationError( "Property '%1$s': cannot find StringConverter for '%2$s'".formatted( property.getPropertyName(), elementType.toString() ) ) ); 203 204 switch( determineStringConverterInstantiation( stringConverter, property.hasFlag( ELEMENTTYPE_IS_ENUM ) ) ) 205 { 206 case BY_INSTANCE -> builder.addStatement( "final $1T retValue = new $2T<>( lambda, $3T.INSTANCE )", handlerType, SimpleCmdLineValueHandler.class, stringConverter ); 207 case THROUGH_CONSTRUCTOR -> builder.addStatement( "final $1T retValue = new $2T<>( lambda, new $3T() )", handlerType, SimpleCmdLineValueHandler.class, stringConverter ); 208 case AS_ENUM -> builder.addStatement( "final $1T retValue = new $2T<>( lambda, new $3T( $4T.class ) )", handlerType, SimpleCmdLineValueHandler.class, stringConverter, elementType ); 209 } 210 } 211 } 212 else 213 { 214 //---* The lambda that sets the value to the attribute *----------- 215 lambdaType = ParameterizedTypeName.from( ClassName.from( BiConsumer.class ), ClassName.from( String.class ), property.getPropertyType().box() ); 216 lambda = getComposer().lambdaBuilder() 217 .addParameter( "propertyName" ) 218 .addParameter( "value" ) 219 .addCode( "$N = value", property.getFieldName() ) 220 .build(); 221 222 //---* Retrieve the class for the value handler *------------------ 223 final var valueHandlerClass = retrieveValueHandlerClass( property ); 224 //noinspection OverlyLongLambda 225 valueHandlerClass.ifPresentOrElse( 226 t -> builder.addStatement( "final $T retValue = new $T( lambda ) ", handlerType, t ), 227 () -> 228 { 229 final var stringConverter = property.getStringConverterClass() 230 .orElseThrow( () -> new IllegalAnnotationError( "No String converter for property '%s'".formatted( property.getPropertyName() ) ) ); 231 switch( determineStringConverterInstantiation( stringConverter, property.isEnum() ) ) 232 { 233 case BY_INSTANCE -> builder.addStatement( "final $1T retValue = new $2T<>( lambda, $3T.INSTANCE )", handlerType, SimpleCmdLineValueHandler.class, stringConverter ); 234 case THROUGH_CONSTRUCTOR -> builder.addStatement( "final $1T retValue = new $2T<>( lambda, new $3T() )", handlerType, SimpleCmdLineValueHandler.class, stringConverter ); 235 case AS_ENUM -> builder.addStatement( "final $1T retValue = new $2T<>( lambda, new $3T( $4T.class ) )", handlerType, SimpleCmdLineValueHandler.class, stringConverter, property.getPropertyType() ); 236 } 237 }); 238 } 239 240 //---* Compose the method *-------------------------------------------- 241 final var method = getComposer().methodBuilder( retValue ) 242 .addModifiers( PRIVATE, FINAL ) 243 .addJavadoc( 244 """ 245 Creates the value handler for the property "$L.". 246 """, property.getPropertyName() ) 247 .returns( handlerType, "The value handler." ) 248 .addCode( 249 """ 250 $L 251 """, createSuppressWarningsAnnotation( getComposer(), REDUNDANT_EXPLICIT_VARIABLE_TYPE ) ) 252 .addStatement( "final $T lambda = $L", lambdaType, lambda ) 253 .addCode( builder.build() ) 254 .addCode( getComposer().createReturnStatement() ) 255 .build(); 256 addMethod( method ); 257 258 //---* Done *---------------------------------------------------------- 259 return retValue; 260 } // composeValueHandlerCreation() 261 262 /** 263 * Creates the implementation for the method 264 * {@link org.tquadrat.foundation.config.CLIBeanSpec#dumpParamFileTemplate(OutputStream)}. 265 */ 266 private final void createDumpParamFileTemplate() 267 { 268 final var arg = getComposer().parameterBuilder( OutputStream.class, "outputStream", FINAL ) 269 .build(); 270 final var method = getComposer().methodBuilder( "dumpParamFileTemplate" ) 271 .addModifiers( PUBLIC, FINAL ) 272 .addAnnotation( Override.class ) 273 .addParameter( arg ) 274 .returns( VOID ) 275 .addException( IOException.class ) 276 .addJavadoc( getComposer().createInheritDocComment() ) 277 .addStatement( "$T.dumpParamFileTemplate( $N, $N )", ConfigUtil.class, getField( STD_FIELD_CLIDefinitions ), arg ) 278 .build(); 279 addMethod( method ); 280 } // createDumpParamFileTemplate() 281 282 /** 283 * Creates the implementation for the method 284 * {@link org.tquadrat.foundation.config.CLIBeanSpec#parseCommandLine(String[])}. 285 * 286 * @param registry The registry of the properties that are exposed for 287 * the CLI. 288 * @param errorMsgHolder The field for the parse errors. 289 */ 290 private final void createParseCommandLine( final FieldSpec registry, final FieldSpec errorMsgHolder ) 291 { 292 final TypeName typeName = ArrayTypeName.of( String.class ); 293 final var arg = getComposer().parameterBuilder( typeName, "args", FINAL ) 294 .build(); 295 final var methodBuilder = getComposer().methodBuilder( "parseCommandLine" ) 296 .addModifiers( PUBLIC, FINAL ) 297 .addAnnotation( Override.class ) 298 .addParameter( arg ) 299 .returns( BOOLEAN ) 300 .addJavadoc( getComposer().createInheritDocComment() ) 301 .addStatement( "var retValue = true" ); 302 if( isSynchronized() ) 303 { 304 methodBuilder.beginControlFlow( 305 """ 306 try( final var ignored = $N.lock() ) 307 """, getField( STD_FIELD_WriteLock ) ); 308 } 309 else 310 { 311 methodBuilder.beginControlFlow( 312 """ 313 try 314 """ ); 315 } 316 methodBuilder.addStatement( "$T.parseCommandLine( $N, $N )", ConfigUtil.class, registry, arg ) 317 .addStatement( "$N = null", errorMsgHolder ) 318 .nextControlFlow( 319 """ 320 321 catch( final $T e ) 322 """, CmdLineException.class ) 323 .addStatement( "$N = e.getLocalizedMessage()", errorMsgHolder ) 324 .addStatement( "retValue = false" ) 325 .endControlFlow() 326 .addCode( getComposer().createReturnStatement() ) 327 .build(); 328 329 final var method =methodBuilder.build(); 330 331 addMethod( method ); 332 } // createParseCommandLine() 333 334 /** 335 * Creates the implementation for the method 336 * {@link org.tquadrat.foundation.config.CLIBeanSpec#printUsage(OutputStream, CharSequence)}. 337 * 338 * @param registry The registry of the properties that are exposed for 339 * the CLI. 340 */ 341 private final void createPrintUsage( final FieldSpec registry ) 342 { 343 final var arg0 = getComposer().parameterBuilder( OutputStream.class, "outputStream", FINAL ) 344 .build(); 345 final var arg1 = getComposer().parameterBuilder( CharSequence.class, "command", FINAL ) 346 .build(); 347 final var method = getComposer().methodBuilder( "printUsage" ) 348 .addModifiers( PUBLIC, FINAL ) 349 .addAnnotation( Override.class ) 350 .addParameter( arg0 ) 351 .addParameter( arg1 ) 352 .returns( VOID ) 353 .addException( IOException.class ) 354 .addJavadoc( getComposer().createInheritDocComment() ) 355 .addStatement( "$T.printUsage( $N, $N(), $N, $N )", ConfigUtil.class, arg0, getMethod( STD_METHOD_GetRessourceBundle ), arg1, registry ) 356 .build(); 357 addMethod( method ); 358 } // createPrintUsage() 359 360 /** 361 * Creates the implementation for the method 362 * {@link org.tquadrat.foundation.config.CLIBeanSpec#retrieveParseErrorMessage()}. 363 * 364 * @param errorMsgHolder The field for the parse errors. 365 */ 366 private final void createRetrieveParseErrorMessage( final FieldSpec errorMsgHolder ) 367 { 368 final var typeName = ParameterizedTypeName.from( Optional.class, String.class ); 369 final var method = getComposer().methodBuilder( "retrieveParseErrorMessage" ) 370 .addModifiers( PUBLIC, FINAL ) 371 .addAnnotation( Override.class ) 372 .returns( typeName ) 373 .addJavadoc( getComposer().createInheritDocComment() ) 374 .addStatement( "return $T.ofNullable( $N )", Optional.class, errorMsgHolder ) 375 .build(); 376 addMethod( method ); 377 } // createRetrieveParseErrorMessage() 378 379 /** 380 * Is called by 381 * {@link #build()} 382 * to do the work – only if there is work to do … 383 */ 384 private final void doBuild() 385 { 386 //---* Create the registry for the CLI definitions *------------------- 387 final var registryType = ParameterizedTypeName.from( ClassName.from( List.class ), TypeName.from( CLIDefinition.class ) ); 388 final var registry = getComposer().fieldBuilder( registryType, STD_FIELD_CLIDefinitions.toString(), PRIVATE, FINAL ) 389 .addJavadoc( 390 """ 391 The registry for the CLI definitions 392 """ ) 393 .initializer( "new $T<>()", ArrayList.class ) 394 .build(); 395 addField( STD_FIELD_CLIDefinitions, registry ); 396 397 //---* Create the field for the CLI parsing errors *------------------- 398 final var errorMsgHolder = getComposer().fieldBuilder( String.class, STD_FIELD_CLIError.toString(), PRIVATE ) 399 .addJavadoc( 400 """ 401 The last error message from a call to 402 {@link #parseCommandLine(String[])}. 403 404 @see #retrieveParseErrorMessage() 405 """ ) 406 .initializer( "null" ) 407 .build(); 408 addField( STD_FIELD_CLIError, errorMsgHolder ); 409 410 //---* Add the methods from CLIBeanSpec *------------------------------ 411 createDumpParamFileTemplate(); 412 createParseCommandLine( registry, errorMsgHolder ); 413 createPrintUsage( registry ); 414 createRetrieveParseErrorMessage( errorMsgHolder ); 415 416 /* 417 * The names of previously encountered options, collected to avoid 418 * collisions. 419 */ 420 final Collection<String> alreadyUsedOptions = new HashSet<>(); 421 422 //---* Add the code to the constructor *------------------------------- 423 final var objectType = WildcardTypeName.subtypeOf( Object.class ); 424 final var handlerName = "valueHandler"; 425 final var handlerType = ParameterizedTypeName.from( ClassName.from( CmdLineValueHandler.class ), objectType ); 426 427 final var definitionName = "cliDefinition"; 428 429 final var builder = getComposer().codeBlockBuilder().add( 430 """ 431 432 /* 433 * Initialise the CLI definitions. 434 */ 435 """ ) 436 .addStatement( "$T $L", handlerType, handlerName ) 437 .addStatement( "$T $L", CLIDefinition.class, definitionName ); 438 439 CLIPropertiesLoop: 440 for( final var iterator = getProperties(); iterator.hasNext(); ) 441 { 442 final var property = iterator.next(); 443 if( !property.isOnCLI() ) continue CLIPropertiesLoop; 444 445 //---* Create the value handler *---------------------------------- 446 builder.add( 447 """ 448 449 /* 450 * CLI definition for Property "$L". 451 */ 452 """, property.getPropertyName() 453 ) 454 .addStatement( "$L = $L()", handlerName, composeValueHandlerCreation( property ) ); 455 456 //---* Create the CLI definition *--------------------------------- 457 final var usage = property.getCLIUsage().orElse( null ); 458 final var usageKey = property.getCLIUsageKey().orElse( null ); 459 final var metaVar = property.getCLIMetaVar().orElse( null ); 460 final var required = Boolean.valueOf( property.hasFlag( PROPERTY_CLI_MANDATORY ) ); 461 final var multiValued = Boolean.valueOf( property.hasFlag( PROPERTY_CLI_MULTIVALUED ) ); 462 final var format = property.getCLIFormat().orElse( null ); 463 if( property.hasFlag( PROPERTY_IS_ARGUMENT ) ) 464 { 465 builder.addStatement( "$1L = new $2T( $3S, $4L, $5S, $6S, $7S, $8L, $9L, $10L, $11S )", 466 definitionName, 467 CLIArgumentDefinition.class, 468 property.getPropertyName(), 469 Integer.valueOf( property.getCLIArgumentIndex().orElseThrow( () -> new IllegalAnnotationError( format( MSG_NoArgumentIndex, property.getPropertyName() ) ) ) ), 470 usage, 471 usageKey, 472 metaVar, 473 required, 474 handlerName, 475 multiValued, 476 format ); 477 } 478 else if( property.hasFlag( PROPERTY_IS_OPTION ) ) 479 { 480 final var optionNames = property.getCLIOptionNames().orElseThrow( () -> new IllegalAnnotationError( format( MSG_NoOptionName, property.getPropertyName() ) ) ); 481 for( final var optionName : optionNames ) 482 { 483 if( !alreadyUsedOptions.add( optionName ) ) 484 { 485 throw new IllegalAnnotationError( format( MSG_DuplicateOptionName, optionName, property.getPropertyName() ) ); 486 } 487 } 488 final var names = optionNames.stream() 489 .map( s -> format( "\"%s\"", s ) ) 490 .collect( joining( ", ", "List.of( ", " )" ) ); 491 builder.addStatement( "$1L = new $2T( $3S, $4L, $5S, $6S, $7S, $8L, $9L, $10L, $11S )", 492 definitionName, 493 CLIOptionDefinition.class, 494 property.getPropertyName(), 495 names, 496 usage, 497 usageKey, 498 metaVar, 499 required, 500 handlerName, 501 multiValued, 502 format ); 503 } 504 else 505 { 506 throw new IllegalAnnotationError( format( MSG_InvalidCLIType, property.getPropertyName() ) ); 507 } 508 builder.addStatement( "$N.add( $L )", registry, definitionName ); 509 } 510 addConstructorCode( builder.build() ); 511 } // doBuild() 512 513 /** 514 * <p>{@summary Retrieves the class for the value handler for the given 515 * property.} If returning 516 * {@link Optional#empty() empty}, 517 * an instance of 518 * {@link SimpleCmdLineValueHandler} 519 * must be used that will be instantiated with the 520 * {@link org.tquadrat.foundation.lang.StringConverter} 521 * retrieved by a call to 522 * {@link PropertySpec#getStringConverterClass()}.</p> 523 * 524 * @param property The property. 525 * @return An instance of 526 * {@link Optional} 527 * that holds the respective type name. 528 */ 529 private final Optional<TypeName> retrieveValueHandlerClass( final PropertySpec property ) 530 { 531 var retValue = requireNonNullArgument( property, "property" ).getCLIValueHandlerClass(); 532 if( retValue.isEmpty() ) 533 { 534 final var handlerClass = m_HandlerClasses.get( property.getPropertyType() ); 535 if( nonNull( handlerClass ) ) 536 { 537 retValue = Optional.of( handlerClass ); 538 } 539 } 540 ifDebug( a -> 541 { 542 final var propertyName = ((PropertySpec) a [0]).getPropertyName(); 543 final var propertyType = ((PropertySpec) a [0]).getPropertyType().toString(); 544 //noinspection unchecked 545 final var handlerClass = Objects.toString( ((Optional<TypeName>) a [1]).orElse( null ) ); 546 return format( "property: %1$s%n\tpropertyType: %2$s%n\thandlerClass: %3$s", propertyName, propertyType, handlerClass ); 547 }, property, retValue ); 548 549 //---* Done *---------------------------------------------------------- 550 return retValue; 551 } // retrieveValueHandlerClass() 552} 553// class CLIBeanBuilder 554 555/* 556 * End of File 557 */