001/*
002 * ============================================================================
003 *  Copyright © 2002-2021 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;
019
020import static java.util.Arrays.copyOfRange;
021import static org.apiguardian.api.API.Status.INTERNAL;
022import static org.tquadrat.foundation.config.ap.CollectionKind.NO_COLLECTION;
023import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.ENVIRONMENT_VARIABLE;
024import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.GETTER_IS_DEFAULT;
025import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.PROPERTY_IS_SPECIAL;
026import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.SYSTEM_PREFERENCE;
027import static org.tquadrat.foundation.config.ap.PropertySpec.PropertyFlag.SYSTEM_PROPERTY;
028import static org.tquadrat.foundation.config.ap.impl.codebuilders.CodeGeneratorContext.getAddMethodComposer;
029import static org.tquadrat.foundation.config.ap.impl.codebuilders.CodeGeneratorContext.getConstructorFragment4EnvironmentComposer;
030import static org.tquadrat.foundation.config.ap.impl.codebuilders.CodeGeneratorContext.getConstructorFragment4SystemPreferenceComposer;
031import static org.tquadrat.foundation.config.ap.impl.codebuilders.CodeGeneratorContext.getConstructorFragment4SystemPropComposer;
032import static org.tquadrat.foundation.config.ap.impl.codebuilders.CodeGeneratorContext.getFieldComposer;
033import static org.tquadrat.foundation.config.ap.impl.codebuilders.CodeGeneratorContext.getGetterComposer;
034import static org.tquadrat.foundation.config.ap.impl.codebuilders.CodeGeneratorContext.getSetterComposer;
035import static org.tquadrat.foundation.lang.CommonConstants.NUL;
036import static org.tquadrat.foundation.lang.Objects.isNull;
037import static org.tquadrat.foundation.lang.Objects.mapNonNull;
038import static org.tquadrat.foundation.lang.Objects.nonNull;
039import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument;
040import static org.tquadrat.foundation.lang.Objects.requireNotEmptyArgument;
041import static org.tquadrat.foundation.util.StringUtils.isEmptyOrBlank;
042import static org.tquadrat.foundation.util.StringUtils.isNotEmptyOrBlank;
043
044import javax.lang.model.element.ExecutableElement;
045import javax.lang.model.element.Name;
046import java.util.EnumSet;
047import java.util.List;
048import java.util.Optional;
049import java.util.OptionalInt;
050import java.util.function.BiFunction;
051
052import org.apiguardian.api.API;
053import org.tquadrat.foundation.annotation.ClassVersion;
054import org.tquadrat.foundation.config.INIValue;
055import org.tquadrat.foundation.config.SpecialPropertyType;
056import org.tquadrat.foundation.config.ap.CollectionKind;
057import org.tquadrat.foundation.config.ap.PropertySpec;
058import org.tquadrat.foundation.javacomposer.CodeBlock;
059import org.tquadrat.foundation.javacomposer.FieldSpec;
060import org.tquadrat.foundation.javacomposer.JavaComposer;
061import org.tquadrat.foundation.javacomposer.MethodSpec;
062import org.tquadrat.foundation.javacomposer.TypeName;
063
064/**
065 *  The implementation for
066 *  {@link PropertySpec}.
067 *
068 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
069 *  @version $Id: PropertySpecImpl.java 1053 2023-03-11 00:10:49Z tquadrat $
070 *  @since 0.1.0
071 *  @UMLGraph.link
072 */
073@SuppressWarnings( {"ClassWithTooManyFields", "OverlyComplexClass"} )
074@ClassVersion( sourceVersion = "$Id: PropertySpecImpl.java 1053 2023-03-11 00:10:49Z tquadrat $" )
075@API( status = INTERNAL, since = "0.1.0" )
076public final class PropertySpecImpl implements PropertySpec
077{
078       /*------------*\
079    ====** Attributes **=======================================================
080        \*------------*/
081    /**
082     *  The name for the 'add' method's argument.
083     */
084    private Name m_AddMethodArgumentName = null;
085
086    /**
087     *  The builder for the 'add' method of this property.
088     */
089    private MethodSpec.Builder m_AddMethodBuilder = null;
090
091    /**
092     *  The method that creates the 'add' method for this property.
093     */
094    private BiFunction<CodeBuilder,PropertySpecImpl,MethodSpec> m_AddMethodComposer;
095
096    /**
097     *  The name for the 'add'' method.
098     */
099    private Name m_AddMethodName;
100
101    /**
102     *  The argument index.
103     *
104     *  @see org.tquadrat.foundation.config.Argument#index()
105     */
106    private int m_CLIArgumentIndex = -1; // -1 means that the value was not set
107
108    /**
109     *  The special CLI format.
110     *
111     *  @see org.tquadrat.foundation.config.Argument#format()
112     *  @see org.tquadrat.foundation.config.Option#format()
113     */
114    private String m_CLIFormat = null;
115
116    /**
117     *  The CLI meta variable.
118     *
119     *  @see org.tquadrat.foundation.config.Argument#metaVar()
120     *  @see org.tquadrat.foundation.config.Option#metaVar()
121     */
122    private String m_CLIMetaVar = null;
123
124    /**
125     *  The names for a CLI option.
126     *
127     *  @see org.tquadrat.foundation.config.Option#name()
128     *  @see org.tquadrat.foundation.config.Option#aliases()
129     */
130    private List<String> m_CLIOptionNames = null;
131
132    /**
133     *  The CLI value handler class.
134     *
135     *  @see org.tquadrat.foundation.config.Argument#handler()
136     *  @see org.tquadrat.foundation.config.Option#handler()
137     */
138    private TypeName m_CLIValueHandlerClass = null;
139
140    /**
141     *  The CLI usage text.
142     *
143     *  @see org.tquadrat.foundation.config.Argument#usage()
144     *  @see org.tquadrat.foundation.config.Option#usage()
145     */
146    private String m_CLIUsage = null;
147
148    /**
149     *  The CLI usage key.
150     *
151     *  @see org.tquadrat.foundation.config.Argument#usageKey()
152     *  @see org.tquadrat.foundation.config.Option#usageKey()
153     */
154    private String m_CLIUsageKey = null;
155
156    /**
157     *  The kind of collection for this property.
158     */
159    private CollectionKind m_CollectionKind = NO_COLLECTION;
160
161    /**
162     *  The method that creates the constructor fragment for the initialisation
163     *  of this property.
164     */
165    private BiFunction<CodeBuilder,PropertySpecImpl, CodeBlock> m_ConstructorFragmentComposer;
166
167    /**
168     *  The default value for environment variables or system properties.
169     */
170    private String m_EnvironmentDefaultValue = null;
171
172    /**
173     *  The name of the environment variable that is used to initialise this
174     *  property.
175     */
176    private String m_EnvironmentVariableName = null;
177
178    /**
179     *  The method that creates the field for this property.
180     */
181    private BiFunction<CodeBuilder,PropertySpecImpl,FieldSpec> m_FieldComposer;
182
183    /**
184     *  The name of the field for the property.
185     */
186    private String m_FieldName = null;
187
188    /**
189     *  The builder for the getter of this property.
190     */
191    private MethodSpec.Builder m_GetterBuilder = null;
192
193    /**
194     *  The method that creates the getter for this property.
195     */
196    private BiFunction<CodeBuilder,PropertySpecImpl,MethodSpec> m_GetterComposer;
197
198    /**
199     *  The name for the getter method.
200     */
201    private Name m_GetterMethodName = null;
202
203    /**
204     *  The return type for the getter method.
205     */
206    private TypeName m_GetterReturnType;
207
208    /**
209     *  The comment for this property when stored in an {@code INI} file.
210     */
211    private String m_INIComment = null;
212
213    /**
214     *  The group for this property when stored in an {@code INI} file.
215     */
216    private String m_INIGroup = null;
217
218    /**
219     *  The key for this property when stored in an {@code INI} file.
220     */
221    private String m_INIKey = null;
222
223    /**
224     *  The flag that indicates whether the property type is an {@code enum}
225     *  type.
226     */
227    private boolean m_IsEnum = false;
228
229    /**
230     *  <p>{@summary The {@code Preferences} accessor class.}</p>
231     *  <p>This is used when this property is linked to a preference, but also
232     *  to initialise it from a SYSTEM preference.</p>
233     *
234     *  @see org.tquadrat.foundation.config.Preference#accessor()
235     *  @see org.tquadrat.foundation.config.SystemPreference#accessor()
236     */
237    private TypeName m_PrefsAccessorClass = null;
238
239    /**
240     *  <p>{@summary The {@code Preferences} key.}</p>
241     *  <p>This is used when this property is linked to a preference, but also
242     *  to initialise it from a SYSTEM preference. In first case, the name is
243     *  defaulted to the property name, while it is mandatory otherwise.</p>
244     *
245     *  @see org.tquadrat.foundation.config.Preference#key()
246     *  @see org.tquadrat.foundation.config.SystemPreference#key()
247     */
248    private String m_PrefsKey = null;
249
250    /**
251     *  The property flags.
252     */
253    private final EnumSet<PropertyFlag> m_PropertyFlags = EnumSet.noneOf( PropertyFlag.class );
254
255    /**
256     *  The name of the property.
257     */
258    private final String m_PropertyName;
259
260    /**
261     *  The type of the property.
262     */
263    private TypeName m_PropertyType;
264
265    /**
266     *  The name for the setter's argument.
267     */
268    private Name m_SetterArgumentName = null;
269
270    /**
271     *  The builder for the setter of this property.
272     */
273    private MethodSpec.Builder m_SetterBuilder = null;
274
275    /**
276     *  The method that creates the setter for this property.
277     */
278    private BiFunction<CodeBuilder,PropertySpecImpl,MethodSpec> m_SetterComposer;
279
280    /**
281     *  The name for the setter method.
282     */
283    private Name m_SetterMethodName;
284
285    /**
286     *  The speciality type for this property; usually, this is {@code null}.
287     */
288    private SpecialPropertyType m_SpecialPropertyType = null;
289
290    /**
291     *  The class that implements the String converter for the type of this
292     *  property.
293     */
294    private TypeName m_StringConverterClass = null;
295
296    /**
297     *  The path for the SYSTEM preferences node that holds the initialisation
298     *  data for this property.
299     *
300     *  @see org.tquadrat.foundation.config.SystemPreference#path()
301     */
302    private String m_SystemPrefsPath = null;
303
304    /**
305     *  The name of the system property that is used to initialise this
306     *  property.
307     */
308    private String m_SystemPropertyName = null;
309
310        /*------------------------*\
311    ====** Static Initialisations **===========================================
312        \*------------------------*/
313
314        /*--------------*\
315    ====** Constructors **=====================================================
316        \*--------------*/
317    /**
318     *  Creates a new {@code PropertySpecImpl} instance.
319     *
320     *  @param  propertyName    The name of the property.
321     */
322    public PropertySpecImpl( final String propertyName )
323    {
324        m_PropertyName = requireNotEmptyArgument( propertyName, "propertyName" );
325    }   //  PropertySpecImpl()
326
327        /*---------*\
328    ====** Methods **==========================================================
329        \*---------*/
330    /**
331     *  {@inheritDoc}
332     */
333    @Override
334    public final Optional<MethodSpec> createAddMethod( final CodeBuilder codeBuilder )
335    {
336        final Optional<MethodSpec> retValue = isNull( m_AddMethodComposer ) || hasFlag( GETTER_IS_DEFAULT )
337            ? Optional.empty()
338            : Optional.of( m_AddMethodComposer.apply( requireNonNullArgument( codeBuilder, "codeBuilder" ), this ) );
339
340        //---* Done *----------------------------------------------------------
341        return retValue;
342    }   //  createAddMethod()
343
344    /**
345     *  {@inheritDoc}
346     */
347    @Override
348    public Optional<CodeBlock> createConstructorFragment( final CodeBuilder codeBuilder )
349    {
350        final Optional<CodeBlock> retValue = isNull( m_ConstructorFragmentComposer ) || hasFlag( GETTER_IS_DEFAULT )
351            ? Optional.empty()
352            : Optional.of( m_ConstructorFragmentComposer.apply( requireNonNullArgument( codeBuilder, "codeBuilder" ), this ) );
353
354        //---* Done *----------------------------------------------------------
355        return retValue;
356    }   //  createConstructorFragment()
357
358    /**
359     *  {@inheritDoc}
360     */
361    @Override
362    public final Optional<FieldSpec> createField( final CodeBuilder codeBuilder )
363    {
364        final Optional<FieldSpec> retValue = isNull( m_FieldComposer ) || hasFlag( GETTER_IS_DEFAULT )
365            ? Optional.empty()
366            : Optional.of( m_FieldComposer.apply( requireNonNullArgument( codeBuilder, "codeBuilder" ), this ) );
367
368        //---* Done *----------------------------------------------------------
369        return retValue;
370    }   //  createField()
371
372    /**
373     *  {@inheritDoc}
374     */
375    @Override
376    public Optional<MethodSpec> createGetter( final CodeBuilder codeBuilder )
377    {
378        final Optional<MethodSpec> retValue = isNull( m_GetterComposer ) || hasFlag( GETTER_IS_DEFAULT )
379            ? Optional.empty()
380            : Optional.of( m_GetterComposer.apply( requireNonNullArgument( codeBuilder, "codeBuilder" ), this ) );
381
382        //---* Done *----------------------------------------------------------
383        return retValue;
384    }   //  createGetter()
385
386    /**
387     *  {@inheritDoc}
388     */
389    @Override
390    public Optional<MethodSpec> createSetter( final CodeBuilder codeBuilder )
391    {
392        final Optional<MethodSpec> retValue = isNull( m_SetterComposer ) || hasFlag( GETTER_IS_DEFAULT )
393            ? Optional.empty()
394            : Optional.of( m_SetterComposer.apply( requireNonNullArgument( codeBuilder, "codeBuilder" ), this ) );
395
396        //---* Done *----------------------------------------------------------
397        return retValue;
398    }   //  createSetter()
399
400    /**
401     *  {@inheritDoc}
402     */
403    @Override
404    public final Name getAddMethodArgumentName() { return m_AddMethodArgumentName; }
405
406    /**
407     *  <p>{@summary Returns a builder for the 'add' method for this
408     *  property.}</p>
409     *  <p>This is a convenience method that allows to benefit from
410     *  {@link JavaComposer#createMethod(ExecutableElement)}.</p>
411     *
412     *  @return An instance of
413     *      {@link Optional}
414     *      that holds the builder.
415     */
416    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
417    public final Optional<MethodSpec.Builder> getAddMethodBuilder() { return Optional.ofNullable( m_AddMethodBuilder ); }
418
419    /**
420     *  {@inheritDoc}
421     */
422    @Override
423    public final Optional<Name> getAddMethodName() { return Optional.ofNullable( m_AddMethodName ); }
424
425    /**
426     *  {@inheritDoc}
427     */
428    @Override
429    public final OptionalInt getCLIArgumentIndex() { return m_CLIArgumentIndex < 0 ? OptionalInt.empty() : OptionalInt.of( m_CLIArgumentIndex ); }
430
431    /**
432     *  {@inheritDoc}
433     */
434    @Override
435    public final Optional<String> getCLIFormat() { return Optional.ofNullable( m_CLIFormat ); }
436
437    /**
438     *  {@inheritDoc}
439     */
440    @Override
441    public final Optional<String> getCLIMetaVar() { return Optional.ofNullable( m_CLIMetaVar ); }
442
443    /**
444     *  {@inheritDoc}
445     */
446    @SuppressWarnings( "OptionalContainsCollection" )
447    @Override
448    public final Optional<List<String>> getCLIOptionNames() { return Optional.ofNullable( m_CLIOptionNames ); }
449
450    /**
451     *  {@inheritDoc}
452     */
453    @Override
454    public final Optional<String> getCLIUsage() { return Optional.ofNullable( m_CLIUsage ); }
455
456    /**
457     *  {@inheritDoc}
458     */
459    @Override
460    public final Optional<String> getCLIUsageKey() { return Optional.ofNullable( m_CLIUsageKey ); }
461
462    /**
463     *  {@inheritDoc}
464     */
465    @Override
466    public final Optional<TypeName> getCLIValueHandlerClass() { return Optional.ofNullable( m_CLIValueHandlerClass ); }
467
468    /**
469     *  {@inheritDoc}
470     */
471    @Override
472    public final CollectionKind getCollectionKind() { return m_CollectionKind; }
473
474    /**
475     *  {@inheritDoc}
476     */
477    @Override
478    public final Optional<String> getEnvironmentDefaultValue() { return Optional.ofNullable( m_EnvironmentDefaultValue ); }
479
480    /**
481     *  {@inheritDoc}
482     */
483    @Override
484    public final Optional<String> getEnvironmentVariableName() { return Optional.ofNullable( m_EnvironmentVariableName ); }
485
486    /**
487     *  {@inheritDoc}
488     */
489    @Override
490    public final String getFieldName()
491    {
492        final var retValue = isEmptyOrBlank( m_FieldName ) ? PropertySpec.super.getFieldName() : m_FieldName;
493
494        //---* Done *----------------------------------------------------------
495        return retValue;
496    }   //  getFieldName()
497
498    /**
499     *  {@inheritDoc}
500     */
501    @Override
502    public final Optional<MethodSpec.Builder> getGetterBuilder() { return Optional.ofNullable( m_GetterBuilder ); }
503
504    /**
505     *  {@inheritDoc}
506     */
507    @Override
508    public final Optional<Name> getGetterMethodName() { return Optional.ofNullable( m_GetterMethodName ); }
509
510    /**
511     *  {@inheritDoc}
512     */
513    @Override
514    public final TypeName getGetterReturnType() { return m_GetterReturnType; }
515
516    /**
517     *  {@inheritDoc}
518     */
519    @Override
520    public Optional<String> getINIComment() { return Optional.ofNullable( m_INIComment ); }
521
522    /**
523     *  {@inheritDoc}
524     */
525    @Override
526    public Optional<String> getINIGroup() { return Optional.ofNullable( m_INIGroup ); }
527
528    /**
529     *  {@inheritDoc}
530     */
531    @Override
532    public Optional<String> getINIKey() { return Optional.ofNullable( m_INIKey ); }
533
534    /**
535     *  {@inheritDoc}
536     */
537    @Override
538    public final Optional<TypeName> getPrefsAccessorClass() { return Optional.ofNullable( m_PrefsAccessorClass ); }
539
540    /**
541     *  {@inheritDoc}
542     */
543    @Override
544    public final Optional<String> getPrefsKey() { return Optional.ofNullable( m_PrefsKey ); }
545
546    /**
547     *  {@inheritDoc}
548     */
549    @Override
550    public final String getPropertyName() { return m_PropertyName; }
551
552    /**
553     *  {@inheritDoc}
554     */
555    @Override
556    public final TypeName getPropertyType() { return m_PropertyType; }
557
558    /**
559     *  {@inheritDoc}
560     */
561    @Override
562    public final Name getSetterArgumentName() { return m_SetterArgumentName; }
563
564    /**
565     *  {@inheritDoc}
566     */
567    @Override
568    public final Optional<MethodSpec.Builder> getSetterBuilder() { return Optional.ofNullable( m_SetterBuilder ); }
569
570    /**
571     *  {@inheritDoc}
572     */
573    @Override
574    public final Optional<Name> getSetterMethodName() { return Optional.ofNullable( m_SetterMethodName ); }
575
576    /**
577     *  {@inheritDoc}
578     */
579    @Override
580    public final Optional<SpecialPropertyType> getSpecialPropertyType() { return Optional.ofNullable( m_SpecialPropertyType ); }
581
582    /**
583     *  {@inheritDoc}
584     */
585    @Override
586    public final Optional<TypeName> getStringConverterClass() { return Optional.ofNullable( m_StringConverterClass ); }
587
588    /**
589     *  {@inheritDoc}
590     */
591    @Override
592    public Optional<String> getSystemPrefsPath()
593    {
594        return Optional.ofNullable( m_SystemPrefsPath );
595    }   //  getSystemPrefsPath()
596
597    /**
598     *  {@inheritDoc}
599     */
600    @Override
601    public final Optional<String> getSystemPropertyName() { return Optional.ofNullable( m_SystemPropertyName ); }
602
603    /**
604     *  {@inheritDoc}
605     */
606    @Override
607    public final boolean isEnum() { return m_IsEnum; }
608
609    /**
610     *  {@inheritDoc}
611     */
612    @Override
613    public final boolean hasFlag( final PropertyFlag flag ) { return m_PropertyFlags.contains( requireNonNullArgument( flag, "flag" ) ); }
614
615    /**
616     *  {@inheritDoc}
617     */
618    @Override
619    public final PropertySpec merge()
620    {
621        final PropertySpecImpl retValue;
622        if( hasFlag( PROPERTY_IS_SPECIAL ) )
623        {
624            retValue = new PropertySpecImpl( m_SpecialPropertyType.getPropertyName() );
625            final var otherSpec = CodeGenerator.getSpecialPropertySpecification( m_SpecialPropertyType );
626
627            final var flags = EnumSet.copyOf( m_PropertyFlags );
628            flags.addAll( otherSpec.getAllFlags() );
629            retValue.setFlag( flags.toArray( PropertyFlag []::new ) );
630
631            retValue.m_AddMethodArgumentName = m_AddMethodArgumentName;
632            getAddMethodBuilder().ifPresent( retValue::setAddMethodBuilder );
633            retValue.m_AddMethodComposer = otherSpec.getAddMethodComposer().orElse( m_AddMethodComposer );
634            getAddMethodName().ifPresent( retValue::setAddMethodName );
635            getCLIArgumentIndex().ifPresent( retValue::setCLIArgumentIndex );
636            getCLIFormat().ifPresent( retValue::setCLIFormat );
637            getCLIMetaVar().ifPresent( retValue::setCLIMetaVar );
638            getCLIOptionNames().ifPresent( retValue::setCLIOptionNames );
639            getCLIUsage().ifPresent( retValue::setCLIUsage );
640            getCLIUsageKey().ifPresent( retValue::setCLIUsageKey );
641            getCLIValueHandlerClass().ifPresent( retValue::setCLIValueHandlerClass );
642            retValue.setCollectionKind( otherSpec.getCollectionKind() );
643            retValue.m_ConstructorFragmentComposer = otherSpec.getConstructorFragmentComposer().orElse( m_ConstructorFragmentComposer );
644            otherSpec.getEnvironmentDefaultValue().ifPresent( retValue::setEnvironmentDefaultValue );
645            otherSpec.getEnvironmentVariableName().ifPresentOrElse( retValue::setEnvironmentVariableName, () -> retValue.m_EnvironmentVariableName = null );
646            retValue.setFieldName( otherSpec.getFieldName() ); // Has a side effect in setting retValue.m_FieldComposer …
647            retValue.m_FieldComposer = otherSpec.getFieldComposer().orElse( m_FieldComposer );
648            getGetterBuilder().ifPresent( retValue::setGetterBuilder );
649            getGetterMethodName().ifPresent( retValue::setGetterMethodName );
650            retValue.m_GetterComposer = otherSpec.getGetterComposer().orElse( m_GetterComposer );
651            retValue.setGetterReturnType( otherSpec.getGetterReturnType() );
652            otherSpec.getINIComment().ifPresent( s -> retValue.m_INIComment = s );
653            otherSpec.getINIGroup().ifPresent( g -> retValue.m_INIGroup = g );
654            otherSpec.getINIKey().ifPresent( s -> retValue.m_INIKey = s );
655            retValue.setIsEnum( otherSpec.isEnum() );
656            otherSpec.getPrefsAccessorClass().ifPresent( retValue::setPrefsAccessorClass );
657            otherSpec.getPrefsKey().ifPresent( retValue::setPrefsKey );
658            retValue.setPropertyType( otherSpec.getPropertyType() );
659            retValue.m_SetterArgumentName = m_SetterArgumentName;
660            otherSpec.getSystemPrefsPath().ifPresent( retValue::setSystemPrefsPath );
661            getSetterBuilder().ifPresent( retValue::setSetterBuilder );
662            retValue.m_SetterComposer = otherSpec.getSetterComposer().orElse( m_SetterComposer );
663            getSetterMethodName().ifPresent( retValue::setSetterMethodName );
664            retValue.setSpecialPropertyType( m_SpecialPropertyType );
665            otherSpec.getStringConverterClass().ifPresent( retValue::setStringConverterClass );
666            otherSpec.getSystemPropertyName().ifPresentOrElse( retValue::setSystemPropertyName, () -> retValue.m_SystemPropertyName = null );
667        }
668        else
669        {
670            retValue = this;
671        }
672
673        //---* Done *----------------------------------------------------------
674        return retValue;
675    }   //  merge()
676
677    /**
678     *  Sets the name for the 'add' method's argument.
679     *
680     *  @param  name    The name of the argument.
681     */
682    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
683    public final void setAddMethodArgumentName( final Name name )
684    {
685        m_AddMethodArgumentName = requireNonNullArgument( name, "name" );
686    }   //  setAddMethodArgumentName()
687
688    /**
689     *  Sets the builder for the 'add' method of this property.
690     *
691     *  @param  builder The builder; can be {@code null}.
692     */
693    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
694    public final void setAddMethodBuilder( final MethodSpec.Builder builder )
695    {
696        m_AddMethodBuilder = builder;
697    }   //  setAddMethodBuilder()
698
699    /**
700     *  Sets the name of the 'add'' method for this property.
701     *
702     *  @param  name    The name of the setter method.
703     */
704    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
705    public final void setAddMethodName( final Name name )
706    {
707        m_AddMethodName = requireNotEmptyArgument( name, "name" );
708        m_AddMethodComposer = getAddMethodComposer();
709    }   //  setAddMethodName()
710
711    /**
712     *  Sets the index for an argument on the command line.
713     *
714     *  @param  index   The index, starting by 0. A negative value indicates,
715     *      that the value was not set.
716     *
717     *  @see org.tquadrat.foundation.config.Argument#index()
718     */
719    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
720    public final void setCLIArgumentIndex( final int index ) { m_CLIArgumentIndex = index; }
721
722    /**
723     *  Sets the special CLI format.
724     *
725     *  @param  format  The format String; can be {@code null}.
726     *
727     *  @see org.tquadrat.foundation.config.Argument#format()
728     *  @see org.tquadrat.foundation.config.Option#format()
729     */
730    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
731    public final void setCLIFormat( final String format ) { m_CLIFormat = format; }
732
733    /**
734     *  Sets the CLI meta variable.
735     *
736     *  @param  metaVar The meta variable; can be {@code null}.
737     *
738     *  @see org.tquadrat.foundation.config.Argument#metaVar()
739     *  @see org.tquadrat.foundation.config.Option#metaVar()
740     */
741    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
742    public final void setCLIMetaVar( final String metaVar ) { m_CLIMetaVar = metaVar; }
743
744    /**
745     *  <p>{@summary Sets the CLI option names.}</p>
746     *  <p>The first entry of the list is the option name, the others are the
747     *  aliases.</p>
748     *
749     *  @param  names   The name and the aliases for the CLI option for this
750     *      property; can be {@code null}, but may not be empty.
751     *
752     *  @see org.tquadrat.foundation.config.Option#name()
753     *  @see org.tquadrat.foundation.config.Option#aliases()
754     */
755    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
756    public final void setCLIOptionNames( final List<String> names )
757    {
758        m_CLIOptionNames = nonNull( names) ? List.copyOf( requireNotEmptyArgument( names, "names" ) ) : null;
759    }   //  setCLIOptionNames()
760
761    /**
762     *  Sets the CLI usage text.
763     *
764     *  @param  text    The usage text; can be {@code null}.
765     *
766     *  @see org.tquadrat.foundation.config.Argument#usage()
767     *  @see org.tquadrat.foundation.config.Option#usage()
768     */
769    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
770    public final void setCLIUsage( final String text )
771    {
772        m_CLIUsage = text;
773    }   //  setCLIUsage()
774
775    /**
776     *  Sets the CLI usage key.
777     *
778     *  @param  key The usage key; can be {@code null}.
779     *
780     *  @see org.tquadrat.foundation.config.Argument#usageKey()
781     *  @see org.tquadrat.foundation.config.Option#usageKey()
782     */
783    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
784    public final void setCLIUsageKey( final String key )
785    {
786        m_CLIUsageKey = key;
787    }   //  setCLIUsageKey()
788
789    /**
790     *  Sets the CLI value handler class.
791     *
792     *  @param  handlerClass    The
793     *      {@link TypeName}
794     *      for the value handler class; can be {@code null}.
795     *
796     *  @see org.tquadrat.foundation.config.Argument#handler()
797     *  @see org.tquadrat.foundation.config.Option#handler()
798     *  @see org.tquadrat.foundation.config.cli.CmdLineValueHandler
799     */
800    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
801    public final void setCLIValueHandlerClass( final TypeName handlerClass )
802    {
803        m_CLIValueHandlerClass = handlerClass;
804    }   //  setCLIValueHandlerClass()
805
806    /**
807     *  Sets the kind of collection for this property.
808     *
809     *  @param  collectionKind  The kind of collection.
810     */
811    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
812    public final void setCollectionKind( final CollectionKind collectionKind )
813    {
814        m_CollectionKind = requireNonNullArgument( collectionKind, "collectionKind" );
815    }   //  setCollectionKind()
816
817    /**
818     *  <p>{@summary Sets the default value for an environment variable or a
819     *  system property.} This is used to initialise this property when it has
820     *  the annotation
821     *  {@link org.tquadrat.foundation.config.EnvironmentVariable &#64;EnvironmentVariable}
822     *  or
823     *  {@link org.tquadrat.foundation.config.SystemProperty &#64;EnvironmentVariable},
824     *  but no value is provided.</p>
825     *  <p>A default value is mandatory when the annotated property has a
826     *  primitive type.</p>
827     *  <p>A String with the only the {@code NUL} character is treated as
828     *  {@code null}.</p>
829     *
830     *  @param  value   The default value.
831     */
832    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
833    public final void setEnvironmentDefaultValue( final String value )
834    {
835        m_EnvironmentDefaultValue = NUL.equals( value ) ? null : value;
836    }   //  setEnvironmentDefaultValue()
837
838    /**
839     *  Sets the name of the environment variable that provides the (initial)
840     *  value for this property.
841     *
842     *  @param  name    The name of the environment variable.
843     */
844    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
845    public final void setEnvironmentVariableName( final String name )
846    {
847        m_EnvironmentVariableName = name;
848        if( isNotEmptyOrBlank( name ) )
849        {
850            m_PropertyFlags.add( ENVIRONMENT_VARIABLE );
851            m_ConstructorFragmentComposer = getConstructorFragment4EnvironmentComposer();
852        }
853        else
854        {
855            m_PropertyFlags.remove( ENVIRONMENT_VARIABLE );
856            m_ConstructorFragmentComposer = null;
857        }
858    }   //  setEnvironmentVariableName()
859
860    /**
861     *  Sets the name of the field for the property.
862     *
863     *  @param  name    The field name.
864     */
865    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
866    public final void setFieldName( final String name )
867    {
868        m_FieldName = requireNotEmptyArgument( name, "name" );
869        m_FieldComposer = getFieldComposer();
870    }   //  setFieldName()
871
872    /**
873     *  Sets the given flags to the property.
874     *
875     *  @param  flag    The flags to set.
876     */
877    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
878    public final void setFlag( final PropertyFlag... flag )
879    {
880        final var length = requireNonNullArgument( flag, "flag" ).length;
881        switch( length )
882        {
883            case 0: break /* Do nothing */;
884            case 1: m_PropertyFlags.add( flag [0] ); break;
885            case 2: m_PropertyFlags.addAll( EnumSet.of( flag [0], flag [1] ) ); break;
886            default: m_PropertyFlags.addAll( EnumSet.of( flag [0], copyOfRange( flag, 1, length ) ) ); break;
887        }
888    }   //  setFlag()
889
890    /**
891     *  Sets the builder for the getter of this property.
892     *
893     *  @param  builder The builder; can be {@code null}.
894     */
895    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
896    public final void setGetterBuilder( final MethodSpec.Builder builder )
897    {
898        m_GetterBuilder = builder;
899    }   //  setGetterBuilder()
900
901    /**
902     *  Sets the method name for the getter.
903     *
904     *  @param  name    The method name.
905     */
906    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
907    public final void setGetterMethodName( final Name name )
908    {
909        m_GetterMethodName = requireNotEmptyArgument( name, "name" );
910        m_GetterComposer = getGetterComposer();
911    }   //  setGetterMethodName()
912
913    /**
914     *  Sets the return type for the getter.
915     *
916     *  @param  type    The getter's return type.
917     */
918    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
919    public final void setGetterReturnType( final TypeName type )
920    {
921        m_GetterReturnType = requireNonNullArgument( type, "type" );
922    }   //  setGetterReturnType()
923
924    /**
925     *  Sets the {@code INI} file configuration for this property.
926     *
927     *  @param  group   The group.
928     *  @param  key The key.
929     *  @param  comment The comment; can be {@code null}.
930     */
931    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
932    public final void setINIConfiguration( final String group, final String key, final String comment )
933    {
934        m_INIComment = comment;
935        m_INIGroup = requireNotEmptyArgument( group, "group" );
936        m_INIKey = requireNotEmptyArgument( key, "key" );
937    }   //  setINIConfiguration()
938
939    /**
940     *  Sets the {@code INI} file configuration for this property.
941     *
942     *  @param  configuration   The configuration annotation.
943     */
944    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
945    public void setINIConfiguration( final INIValue configuration )
946    {
947        final var group = requireNonNullArgument( configuration, "configuration" ).group();
948        final var key = mapNonNull( configuration.key(), s -> isEmptyOrBlank( s ) ? getPropertyName() : s );
949        final var comment = configuration.comment();
950        setINIConfiguration( group, key, comment );
951    }   //  setINIConfiguration()
952
953    /**
954     *  Sets the flag that indicates whether the property is an
955     *  {@link Enum enum}
956     *  type.
957     *
958     *  @param  flag    {@code true} if the property type is an {@code enum}
959     *      type, {@code false} otherwise.
960     */
961    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
962    public final void setIsEnum( final boolean flag ) { m_IsEnum = flag; }
963
964    /**
965     *  Sets the preferences accessor class for this property.
966     *
967     *  @param  accessorClass   The accessor class; can be {@code null}.
968     */
969    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
970    public final void setPrefsAccessorClass( final TypeName accessorClass )
971    {
972        m_PrefsAccessorClass = accessorClass;
973    }   //  setPrefsAccessorClass()
974
975    /**
976     *  Sets the preferences key for this property.
977     *
978     *  @param  preferenceKey   The key.
979     */
980    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
981    public final void setPrefsKey( final String preferenceKey )
982    {
983        m_PrefsKey = preferenceKey;
984    }   //  setPrefsKey
985
986    /**
987     *  Sets the property type.
988     *
989     *  @param  type    The type of the property.
990     */
991    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
992    public final void setPropertyType( final TypeName type )
993    {
994        m_PropertyType = requireNonNullArgument( type, "type" );
995    }   //  setPropertyType()
996
997    /**
998     *  Sets the name for the setter's argument.
999     *
1000     *  @param  name    The name of the argument.
1001     */
1002    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
1003    public final void setSetterArgumentName( final Name name )
1004    {
1005        m_SetterArgumentName = requireNonNullArgument( name, "name" );
1006    }   //  setSetterArgumentName()
1007
1008    /**
1009     *  Sets the builder for the setter of this property.
1010     *
1011     *  @param  builder The builder; can be {@code null}.
1012     */
1013    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
1014    public final void setSetterBuilder( final MethodSpec.Builder builder )
1015    {
1016        m_SetterBuilder = builder;
1017    }   //  setGetterBuilder()
1018
1019    /**
1020     *  Sets the name of the setter method for this property.
1021     *
1022     *  @param  name    The name of the setter method.
1023     */
1024    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
1025    public void setSetterMethodName( final Name name )
1026    {
1027        m_SetterMethodName = requireNotEmptyArgument( name, "name" );
1028        m_SetterComposer = getSetterComposer();
1029    }   //  setSetterMethodName()
1030
1031    /**
1032     *  Sets the speciality type for this property.
1033     *
1034     *  @param  type    The speciality type.
1035     */
1036    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
1037    public final void setSpecialPropertyType( final SpecialPropertyType type )
1038    {
1039        m_SpecialPropertyType = type;
1040        if( nonNull( m_SpecialPropertyType ) )
1041        {
1042            m_PropertyFlags.add( PROPERTY_IS_SPECIAL );
1043        }
1044        else
1045        {
1046            m_PropertyFlags.remove( PROPERTY_IS_SPECIAL );
1047        }
1048    }   //  setSpecialPropertyType()
1049
1050    /**
1051     *  Sets the name for the class that implements
1052     *  {@link org.tquadrat.foundation.lang.StringConverter}
1053     *  for the type of this property.
1054     *
1055     *  @param  typeName    The String converter class; can be {@code null}.
1056     */
1057    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
1058    public final void setStringConverterClass( final TypeName typeName )
1059    {
1060        m_StringConverterClass = typeName;
1061    }   //  setStringConverterClass()
1062
1063    /**
1064     *  Sets the path for the SYSTEM {@code Preferences} node that holds the
1065     *  initialisation data for this property.
1066     *
1067     *  @param  path    The path.
1068     */
1069    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
1070    public final void setSystemPrefsPath( final String path )
1071    {
1072        m_SystemPrefsPath = path;
1073        if( isNotEmptyOrBlank( path ) )
1074        {
1075            m_PropertyFlags.add( SYSTEM_PREFERENCE );
1076            m_ConstructorFragmentComposer = getConstructorFragment4SystemPreferenceComposer();
1077        }
1078        else
1079        {
1080            m_PropertyFlags.remove( SYSTEM_PREFERENCE );
1081            m_ConstructorFragmentComposer = null;
1082        }
1083    }   //  setSystemPrefsPath()
1084
1085    /**
1086     *  Sets the name of the system property that provides the (initial) value
1087     *  for this property.
1088     *
1089     *  @param  name    The name of the system property.
1090     */
1091    @SuppressWarnings( "PublicMethodNotExposedInInterface" )
1092    public final void setSystemPropertyName( final String name )
1093    {
1094        m_SystemPropertyName = name;
1095        if( isNotEmptyOrBlank( name ) )
1096        {
1097            m_PropertyFlags.add( SYSTEM_PROPERTY );
1098            m_ConstructorFragmentComposer = getConstructorFragment4SystemPropComposer();
1099        }
1100        else
1101        {
1102            m_PropertyFlags.remove( SYSTEM_PROPERTY );
1103            m_ConstructorFragmentComposer = null;
1104        }
1105    }   //  setSystemPropertyName()
1106}
1107//  class PropertySpecImpl
1108
1109/*
1110 *  End of File
1111 */