Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package dk.mada.jaxrs.generator.mpclient;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dk.mada.jaxrs.generator.mpclient.imports.UserMappedImport;
import dk.mada.jaxrs.model.options.OptionReader;
import dk.mada.jaxrs.model.types.Primitive;
import dk.mada.jaxrs.model.types.TypeDateTime;
import dk.mada.jaxrs.model.types.TypeDateTime.DateTimeVariant;
import dk.mada.jaxrs.model.types.TypeName;
/**
* Generator configuration options.
*
* Extracts generator-specific keys from the input properties provided by the user.
*/
public final class GeneratorOpts {
private static final Logger logger = LoggerFactory.getLogger(GeneratorOpts.class);
/** The ID of this generator. */
private static final String GENERATOR_ID = "dk.mada.jaxrs.Generator";
/** Generator option for API package. */
public static final String GENERATOR_API_PACKAGE = "generator-api-package";
/** Generator option for DTO package. */
public static final String GENERATOR_DTO_PACKAGE = "generator-dto-package";
/** Generator option for property conflict resolution. */
public static final String GENERATOR_USE_PROPERTY_CONFLICT_RESOLUTION = "generator-use-property-conflict-resolution";
/** Generator option for no-format number */
public static final String GENERATOR_TYPE_NO_FORMAT_NUMBER = "generator-type-no-format-number";
/** Time that the code was generated. */
private final String generatedAtTime;
/** User options. */
private final OptionReader or;
/** Selects Jackson FasterXml as output format. */
private final boolean useJacksonFasterxml;
/** Selects Jsonb as output format (this is the default). */
private final boolean useJsonb;
/** Selects use of jakarta over javax for JAX-RS types. */
private final boolean useJakarta;
/** Flag for using jspecify nullable annotations. */
private final boolean useJspecify;
/** Flag for rendering DTOs as records. */
private final boolean useRecords;
/** Flag for using OffsetDateTime serializer. */
private final boolean useJacksonOffsetDateTimeSerializer;
/** Flag for using LocalDateTime serializer. */
private final boolean useJacksonLocalDateTimeSerializer;
/** Flag for using LocalDate serializer. */
private final boolean useJacksonLocalDateSerializer;
/** Predicate for record builders to create. */
private final Predicate recordBuilderPredicate;
/**
* Constructs a new instance.
*
* @param or option reader
* @param leakedParserOpts leaked parser options
*/
public GeneratorOpts(OptionReader or, LeakedParserOpts leakedParserOpts) {
this.or = or;
generatedAtTime = LocalDateTime.now()
.withNano(0)
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
useJacksonFasterxml = or.bool("generator-jackson-fasterxml");
boolean willUseJsonb = or.bool("generator-jsonb");
int activatedSerializerApis = 0;
if (useJacksonFasterxml) {
activatedSerializerApis++;
}
if (willUseJsonb) {
activatedSerializerApis++;
}
if (activatedSerializerApis > 1) {
throw new IllegalStateException("Only one serializer API can be enabled!");
}
// jsonb is the default if nothing else specified
if (activatedSerializerApis == 0) {
willUseJsonb = true;
}
useJsonb = willUseJsonb;
useJakarta = or.bool("generator-jakarta");
useRecords = or.bool("generator-dto-records", false);
useJspecify = or.bool("generator-jspecify", useRecords);
useJacksonOffsetDateTimeSerializer = useJacksonFasterxml && leakedParserOpts.isJseOffsetDateTime();
useJacksonLocalDateTimeSerializer = useJacksonFasterxml && leakedParserOpts.isJseLocalDateTime();
useJacksonLocalDateSerializer = useJacksonFasterxml && leakedParserOpts.isJseLocalDate();
String value = or.getDefault("generator-dto-records-use-builder", "none");
RecordBuilderControl control = RecordBuilderControl.from(value);
recordBuilderPredicate = switch (control) {
case ALL -> tn -> true;
case NONE -> tn -> false;
case NAMED -> predicateFromNamed(value);
};
}
/**
* Parser options leaked into the generator. These should be eventually be removed when possible.
*
* @param isJseOffsetDateTime true if the OffsetDateTime DTO is represented by JSE
* @param isJseLocalDateTime true if the LocalDateTime DTO is represented by JSE
* @param isJseLocalDate true if the LocalDate DTO is represented by JSE
*/
public record LeakedParserOpts(boolean isJseOffsetDateTime, boolean isJseLocalDateTime, boolean isJseLocalDate) {
}
/**
* {@return the id of this generator}
*
* Used in the @Generator annotation in all generated files. Keep "dk.mada.jaxrs.Generator" as long as this is the only
* generator. (yes, it is the wrong class, but it does not matter much)
*/
public String generatorId() {
return GENERATOR_ID;
}
/** {@return the package to generate API classes to } */
public String apiPackage() {
return or.getRequired(GENERATOR_API_PACKAGE, "apiPackage");
}
/** {@return the API package in path-form} */
public String apiPackageDir() {
return apiPackage().replace('.', '/');
}
/** {@return the package to generate DTO classes to } */
public String dtoPackage() {
return or.getRequired(GENERATOR_DTO_PACKAGE, "modelPackage");
}
/** {@return the DTO package in path-form} */
public String dtoPackageDir() {
return dtoPackage().replace('.', '/');
}
/** {@return true if rendering for jackson, otherwise false} */
public boolean isJackson() {
return useJacksonFasterxml;
}
/** {@return true if rendering for jsonb, otherwise false} */
public boolean isJsonb() {
return useJsonb;
}
/** {@return true if rendering for jakarta, false if rendering for javax} */
public boolean isJakarta() {
return useJakarta;
}
/** {@return true if rendering with jspecify annotations} */
public boolean isJspecify() {
return useJspecify;
}
/** {@return true if json serializer options should be used} */
public boolean isUseJsonSerializeOptions() {
return getJsonSerializeOptions().isPresent();
}
/** {@return optional json serialize options} */
public Optional getJsonSerializeOptions() {
if (!isJackson()) {
return Optional.empty();
}
return or.get("generator-jackson-json-serialize-options");
}
/** {@return true if a jackson date-time serializer should be rendered} */
public boolean isUseJacksonDateTimeSerializer() {
return useJacksonOffsetDateTimeSerializer && useJacksonLocalDateTimeSerializer;
}
/** {@return true if a jackson LocalDate serializer should be rendered} */
public boolean isUseJacksonLocalDateSerializer() {
return useJacksonLocalDateSerializer;
}
/** {@return true if a jackson LocalDateTime serializer should be rendered} */
public boolean isUseJacksonLocalDateTimeSerializer() {
return useJacksonLocalDateTimeSerializer;
}
/** {@return true if a jackson OffsetDateTime serializer should be rendered} */
public boolean isUseJacksonOffsetDateTimeSerializer() {
return useJacksonOffsetDateTimeSerializer;
}
/** {@return the optional LocalDate wire format for jackson} */
public Optional getJacksonLocalDateWireFormat() {
if (!isUseJacksonLocalDateSerializer()) {
return Optional.empty();
}
return or.getOptDefault("generator-jackson-localdate-wire-format", "ISO_LOCAL_DATE");
}
/** {@return the optional LocalDateTime wire format for jackson} */
public Optional getJacksonLocalDateTimeWireFormat() {
if (!isUseJacksonLocalDateTimeSerializer()) {
return Optional.empty();
}
return or.getOptDefault("generator-jackson-localdatetime-wire-format", "ISO_LOCAL_DATE_TIME");
}
/** {@return the optional jackson LocalDate deserializer class name} */
public Optional getJacksonLocalDateDeserializer() {
if (!isUseJacksonLocalDateSerializer()) {
return Optional.empty();
}
return or.getOptDefault("generator-jackson-localdate-deserializer",
ExtraTemplate.LOCAL_DATE_JACKSON_DESERIALIZER.classname());
}
/** {@return the optional jackson LocalDate serializer class name} */
public Optional getJacksonLocalDateSerializer() {
if (!isUseJacksonLocalDateSerializer()) {
return Optional.empty();
}
return or.getOptDefault("generator-jackson-localdate-serializer",
ExtraTemplate.LOCAL_DATE_JACKSON_SERIALIZER.classname());
}
/** {@return the optional jackson LocalDateTime deserializer class name} */
public Optional getJacksonLocalDateTimeDeserializer() {
if (!isUseJacksonLocalDateSerializer()) {
return Optional.empty();
}
return or.getOptDefault("generator-jackson-localdatetime-deserializer",
ExtraTemplate.LOCAL_DATE_TIME_JACKSON_DESERIALIZER.classname());
}
/** {@return the optional jackson LocalDateTime serializer class name} */
public Optional getJacksonLocalDateTimeSerializer() {
if (!isUseJacksonLocalDateTimeSerializer()) {
return Optional.empty();
}
return or.getOptDefault("generator-jackson-localdatetime-serializer",
ExtraTemplate.LOCAL_DATE_TIME_JACKSON_SERIALIZER.classname());
}
/** {@return the optional jackson OffsetDateTime deserializer class name} */
public Optional getJacksonOffsetDateTimeDeserializer() {
if (!isUseJacksonLocalDateSerializer()) {
return Optional.empty();
}
return or.getOptDefault("generator-jackson-offsetdatetime-deserializer",
ExtraTemplate.OFFSET_DATE_TIME_JACKSON_DESERIALIZER.classname());
}
/** {@return the optional jackson OffsetDateTime serializer class name} */
public Optional getJacksonOffsetDateTimeSerializer() {
if (!isUseJacksonLocalDateTimeSerializer()) {
return Optional.empty();
}
return or.getOptDefault("generator-jackson-offsetdatetime-serializer",
ExtraTemplate.OFFSET_DATE_TIME_JACKSON_SERIALIZER.classname());
}
/** {@return the optional OffsetDateTime wire format for jackson} */
public Optional getJacksonOffsetDateTimeWireFormat() {
if (!isUseJacksonOffsetDateTimeSerializer()) {
return Optional.empty();
}
return or.getOptDefault("generator-jackson-offsetdatetime-wire-format", "ISO_OFFSET_DATE_TIME");
}
/** {@return the optional MP client config key} */
public Optional getMpClientConfigKey() {
return or.get("generator-mp-api-register-rest-client");
}
/** {@return the MP providers} */
public List getMpProviders() {
return or.getListDefault("generator-mp-api-register-providers", "");
}
/** {@return the list of DTO classes to skip when generating code} */
public List getSkippedDtoClasses() {
return or.getListDefault("generator-dto-skip-types", "");
}
/** {@return true if OpenApi @Schema annotations should be added} */
public boolean getUseOpenapiSchema() {
return or.bool("generator-dto-use-openapi-schema", true);
}
/** {@return true if DTOs should be generated as records instead of POJOs} */
public boolean isDtoRecords() {
return useRecords;
}
/** {@return true if record DTOs should include null-checks} */
public boolean isUseRecordsRequireNull() {
return or.bool("generator-dto-records-use-requirenonnull", true);
}
/** {@return a predicate which will define which records to generate builders for} */
public Predicate getRecordBuilderPredicate() {
return recordBuilderPredicate;
}
private Predicate predicateFromNamed(String namedOpt) {
int ix = namedOpt.indexOf(':');
int splitIx = ix + 1;
if (ix == -1 || splitIx > namedOpt.length()) {
throw new IllegalArgumentException("Expected list of type names: '" + namedOpt + "'");
}
List names = OptionReader.splitByComma(namedOpt.substring(splitIx));
logger.debug("Will generate record builders for records named: {}", names);
return tn -> names.contains(tn.name());
}
/**
* Returns mapping of external types.
*
* Maps a (OpenApi document) type name (key) into its fully-qualified class name (value). The keySet serves as a set of
* DTOs *not* to generate.
*
* @return mapping of types names to external type imports
**/
public Map getExternalTypeMapping() {
String s = or.get("generator-map-external-types").orElse("");
if (s.isBlank()) {
return Map.of();
}
Map typeMapping = new HashMap<>();
for (String mapping : s.split(";", -1)) {
int index = mapping.indexOf(":");
if (index == -1 || (index + 1) > mapping.length()) {
throw new IllegalArgumentException("No package-to-types mapping found in '" + s + "'");
}
String pkg = mapping.substring(0, index).trim();
String types = mapping.substring(index + 1).trim();
for (String type : types.split(",", -1)) {
String tt = type.trim();
typeMapping.put(tt, new UserMappedImport(pkg + "." + tt));
}
}
return typeMapping;
}
/** {@return true if the extra-template for a jackson LocalDate deserializer should be added, otherwise false} */
public boolean isAddJacksonLocalDateDeserializerTemplate() {
return getJacksonLocalDateDeserializer()
.map(ExtraTemplate.LOCAL_DATE_JACKSON_DESERIALIZER.classname()::equals)
.orElse(false);
}
/** {@return true if the extra-template for a jackson LocalDate serializer should be added, otherwise false} */
public boolean isAddJacksonLocalDateSerializerTemplate() {
return getJacksonLocalDateSerializer()
.map(ExtraTemplate.LOCAL_DATE_JACKSON_SERIALIZER.classname()::equals)
.orElse(false);
}
/** {@return true if the extra-template for a jackson LocalDateTime deserializer should be added, otherwise false} */
public boolean isAddJacksonLocalDateTimeDeserializerTemplate() {
return getJacksonLocalDateTimeDeserializer()
.map(ExtraTemplate.LOCAL_DATE_TIME_JACKSON_DESERIALIZER.classname()::equals)
.orElse(false);
}
/** {@return true if the extra-template for a jackson LocalDateTime serializer should be added, otherwise false} */
public boolean isAddJacksonLocalDateTimeSerializerTemplate() {
return getJacksonLocalDateTimeSerializer()
.map(ExtraTemplate.LOCAL_DATE_TIME_JACKSON_SERIALIZER.classname()::equals)
.orElse(false);
}
/** {@return true if the extra-template for a jackson OffsetDateTime deserializer should be added, otherwise false} */
public boolean isAddJacksonOffsetDateTimeDeserializerTemplate() {
return getJacksonOffsetDateTimeDeserializer()
.map(ExtraTemplate.OFFSET_DATE_TIME_JACKSON_DESERIALIZER.classname()::equals)
.orElse(false);
}
/** {@return true if the extra-template for a jackson OffsetDateTime serializer should be added, otherwise false} */
public boolean isAddJacksonOffsetDateTimeSerializerTemplate() {
return getJacksonOffsetDateTimeSerializer()
.map(ExtraTemplate.OFFSET_DATE_TIME_JACKSON_SERIALIZER.classname()::equals)
.orElse(false);
}
/** {@return true if bean validation should be used, otherwise false} */
public boolean isUseBeanValidation() {
return or.bool("generator-use-bean-validation", true);
}
/** {@return true if DTOs should be marked with Serializable, otherwise false} */
public boolean isUseSerializable() {
return or.bool("generator-use-serializable", false);
}
/** {@return true if DTOs should use create a toString generating single-line output} */
public boolean isUseSingleLineToString() {
return or.bool("generator-use-single-line-tostring", false);
}
/** {@return true if BigDecimal should be used for double, otherwise use Double} */
public boolean isUseBigDecimalForDouble() {
return or.bool("generator-use-bigdecimal-for-double");
}
/** {@return true if collections should be initialized as empty, otherwise will be null} */
public boolean isUseEmptyCollections() {
return or.bool("generator-use-empty-collections");
}
/** {@return true if date-time should be rendered with ZonedDateTime} */
public boolean isUseZonedDateTime() {
return or.bool("generator-use-zoneddatetime");
}
/** {@return true if date-time should be rendered with LocalDateTime} */
public boolean isUseLocalDateTime() {
return or.bool("generator-use-localdatetime");
}
/** {@return true if boolean getters should use 'get' as prefix, otherwise use 'is'} */
public boolean isUseBooleanGetPrefix() {
return or.bool("generator-use-boolean-get-prefix");
}
/** {@return the time the generation happened if enabled, or null} */
public Optional getGeneratedAtTime() {
if (isShowGenerationTimestamp()) {
return Optional.of(generatedAtTime);
}
return Optional.empty();
}
/** {@return true if the property order annotation should be rendered} */
public boolean isUsePropertyOrderAnnotation() {
return or.bool("generator-use-property-order-annotation", false);
}
private boolean isShowGenerationTimestamp() {
return or.bool("generator-use-generated-timestamp");
}
/** {@return the fully-qualified @Generated annotation class} */
public String getGeneratorAnnotationClass() {
if (or.bool("generator-use-generated-jdk", true)) {
return "@javax.annotation.processing.Generated";
} else {
return "@javax.annotation.Generated";
}
}
/** {@return the property sorting order to use} */
public PropertyOrder getPropertyOrder() {
String order = or.getDefault("generator-use-property-order", PropertyOrder.ALPHABETICAL_NOCASE_ORDER.name());
return PropertyOrder.from(order);
}
/** {@return the property conflict resolution to use} */
public PropertyConflictResolution getPropertyConflictResolution() {
String resolution = or.getDefault(GENERATOR_USE_PROPERTY_CONFLICT_RESOLUTION, PropertyConflictResolution.FAIL.name());
return PropertyConflictResolution.from(resolution);
}
/** {@return true if enumerations should deserialize unknown input to 'unknown_default_open_api'} */
public boolean isUseEnumUnknownDefault() {
return or.bool("generator-use-enum-unknown-default");
}
/**
* {@return true if wrapped primitives should be used in API parameters}
*
* API parameters representing primitives can use the primitives in the API (default) or their wrapper counterparts.
*
* @see resources
* spec
* @see jakarta.ws.rs.DefaultValue
*/
public boolean isUseApiWrappedPrimitives() {
return or.bool("generator-use-api-wrapped-primitives", false);
}
/** {@return the default Api resource name} */
public Optional getDefaultApiName() {
return or.get("generator-api-default-name");
}
/** {@return the primitive type to use for integers without format} */
public String getNoFormatIntegerType() {
return or.get("generator-type-no-format-integer")
.orElse(Primitive.SHORT.name());
}
/** {@return the primitive type to use for numbers without format} */
public String getNoFormatNumberType() {
return or.get(GENERATOR_TYPE_NO_FORMAT_NUMBER)
.orElse(BigDecimal.class.getSimpleName());
}
/** {@return true if generation of API classes should use multipart bodies.} */
public boolean isApiUseMultipartForm() {
return or.bool("generator-api-use-multipartform", false);
}
/** {@return true if generation of API classes should be skipped.} */
public boolean isSkipApiClasses() {
return or.bool("generator-api-skip", false);
}
/** {@return the @Produces media type to use for void methods} */
public Optional getVoidProducesMediaType() {
return or.get("generator-api-void-produces-mediatype");
}
/** {@return list of media types that should be handled as input stream} */
public List getResponseInputStreamMediaTypes() {
return or.getListDefault("generator-api-response-inputstream-mediatypes", "");
}
/** {@return true if @RegisterForReflection should be added to DTOs} */
public boolean isUseRegisterForReflection() {
return or.bool("generator-quarkus-use-register-for-reflection");
}
/** {@return true if the destination folder should be kept during testing} */
public boolean isTestingKeepDestination() {
return or.bool("generator-testing-keep-destination");
}
/**
* Property sorting order.
*/
public enum PropertyOrder {
/** Ordered by appearance in the OpenApi document. */
DOCUMENT_ORDER,
/** Ordered alphabetically. */
ALPHABETICAL_ORDER,
/** Ordered alphabetically, ignoring upper/lower case. */
ALPHABETICAL_NOCASE_ORDER;
/**
* Converts property value enum value.
*
* @param value the input property value
* @return the matching property order enumeration
*/
public static PropertyOrder from(String value) {
String name = value.toUpperCase(Locale.ROOT).replace('-', '_');
for (var po : PropertyOrder.values()) {
if (po.name().equals(name)) {
return po;
}
}
throw new IllegalArgumentException("Unknown PropertyOrder " + name);
}
}
/** {@return the date-time implementation variant to use} */
public TypeDateTime.DateTimeVariant getDateTimeVariant() {
if (isUseZonedDateTime() && isUseLocalDateTime()) {
throw new IllegalArgumentException("You can only select one date-time implementation!");
}
if (isUseZonedDateTime()) {
return DateTimeVariant.ZONED;
}
if (isUseLocalDateTime()) {
return DateTimeVariant.LOCAL;
}
return DateTimeVariant.OFFSET;
}
/**
* The resolution to use when a property has conflicting declarations.
*/
public enum PropertyConflictResolution {
/** Fails the code generator. */
FAIL,
/** Clears the conflicting information. */
CLEAR,
/** Uses the first declaration information. */
FIRST;
// LEAST_RESTRICTIVE
// MOST_RESTRICTIVE
/**
* Converts property value enum value.
*
* @param value the input property value
* @return the matching property order enumeration
*/
public static PropertyConflictResolution from(String value) {
String name = value.toUpperCase(Locale.ROOT).replace('-', '_');
for (var po : PropertyConflictResolution.values()) {
if (po.name().equals(name)) {
return po;
}
}
throw new IllegalArgumentException("Unknown PropertyConflictResolution " + name);
}
}
/**
* Control of builders in records.
*/
public enum RecordBuilderControl {
/** Create builder in all records. */
ALL,
/** Create builder in named records only. */
NAMED,
/** No builders created. */
NONE;
/**
* Converts enum value.
*
* @param value the input configuration value
* @return the matching record builder control
*/
public static RecordBuilderControl from(String value) {
String name = value.toUpperCase(Locale.ROOT).replace('-', '_').replaceAll(":.*", "");
for (var po : RecordBuilderControl.values()) {
if (po.name().equals(name)) {
return po;
}
}
throw new IllegalArgumentException("Unknown RecordBuilderControl " + name);
}
}
}