2 * This file is part of yosql. It is subject to the license terms in the LICENSE file found in the top-level
3 * directory of this distribution and at https://creativecommons.org/publicdomain/zero/1.0/. No part of yosql,
4 * including this file, may be copied, modified, propagated, or distributed except according to the terms contained
7 package wtf
.metio
.yosql
.codegen
.dao
;
9 import com
.squareup
.javapoet
.ClassName
;
10 import com
.squareup
.javapoet
.CodeBlock
;
11 import com
.squareup
.javapoet
.FieldSpec
;
12 import wtf
.metio
.yosql
.codegen
.blocks
.Fields
;
13 import wtf
.metio
.yosql
.codegen
.blocks
.Javadoc
;
14 import wtf
.metio
.yosql
.codegen
.files
.SqlStatementParser
;
15 import wtf
.metio
.yosql
.codegen
.logging
.LoggingGenerator
;
16 import wtf
.metio
.yosql
.internals
.javapoet
.TypicalTypes
;
17 import wtf
.metio
.yosql
.internals
.jdk
.Buckets
;
18 import wtf
.metio
.yosql
.internals
.jdk
.Strings
;
19 import wtf
.metio
.yosql
.models
.configuration
.ResultRowConverter
;
20 import wtf
.metio
.yosql
.models
.configuration
.SqlParameter
;
21 import wtf
.metio
.yosql
.models
.immutables
.ConverterConfiguration
;
22 import wtf
.metio
.yosql
.models
.immutables
.NamesConfiguration
;
23 import wtf
.metio
.yosql
.models
.immutables
.SqlConfiguration
;
24 import wtf
.metio
.yosql
.models
.immutables
.SqlStatement
;
26 import javax
.sql
.DataSource
;
28 import java
.util
.stream
.Collectors
;
29 import java
.util
.stream
.IntStream
;
31 import static java
.util
.function
.Predicate
.not
;
34 * Default implementation of the {@link FieldsGenerator} interface.
36 public final class DefaultFieldsGenerator
implements FieldsGenerator
{
38 private static final String NAME_REGEX
= "([a-z])([A-Z])";
39 private static final String NAME_REPLACEMENT
= "$1_$2";
41 private final ConverterConfiguration converters
;
42 private final NamesConfiguration names
;
43 private final LoggingGenerator logging
;
44 private final Javadoc javadoc
;
45 private final Fields fields
;
47 public DefaultFieldsGenerator(
48 final ConverterConfiguration converters
,
49 final NamesConfiguration names
,
50 final LoggingGenerator logging
,
51 final Javadoc javadoc
,
52 final Fields fields
) {
53 this.converters
= converters
;
55 this.logging
= logging
;
56 this.javadoc
= javadoc
;
61 public Optional
<CodeBlock
> staticInitializer(final List
<SqlStatement
> statements
) {
62 final var builder
= CodeBlock
.builder();
64 .map(SqlStatement
::getConfiguration
)
65 .filter(config
-> Buckets
.hasEntries(config
.parameters()))
66 .forEach(config
-> config
.parameters().stream()
67 .filter(SqlParameter
::hasIndices
)
68 .forEach(parameter
-> addIndexArray(builder
, parameter
, config
)));
69 return Optional
.of(builder
.build());
72 private void addIndexArray(
73 final CodeBlock
.Builder builder
,
74 final SqlParameter parameter
,
75 final SqlConfiguration config
) {
76 builder
.addStatement("$N.put($S, $L)",
77 constantSqlStatementParameterIndexFieldName(config
),
78 parameter
.name().orElseThrow(), // TODO: throw business exception
79 indexArray(parameter
));
82 private static String
indexArray(final SqlParameter param
) {
83 return param
.indices()
86 .flatMap(IntStream
::boxed
)
87 .map(Object
::toString
)
88 .collect(Collectors
.joining(", ", "new int[] { ", " }"));
92 public Iterable
<FieldSpec
> asFields(final List
<SqlStatement
> statements
) {
93 final var repositoryFields
= new ArrayList
<FieldSpec
>(statements
.size() * 2 + 2);
95 repositoryFields
.add(fields
.field(DataSource
.class, names
.dataSource()));
96 if (logging
.isEnabled() && !statements
.isEmpty()) {
97 // doesn't matter which statement we pick since they all end up in the same repository anyway
98 final var firstStatement
= statements
.get(0);
99 loggerField(firstStatement
).ifPresent(repositoryFields
::add
);
101 for (final var statement
: statements
) {
102 if (logging
.isEnabled()) {
103 repositoryFields
.add(asConstantRawSqlField(statement
));
105 repositoryFields
.add(asConstantSqlField(statement
));
106 if (Buckets
.hasEntries(statement
.getConfiguration().parameters())) {
107 repositoryFields
.add(asConstantSqlParameterIndexField(statement
));
110 SqlStatement
.resultConverters(statements
, converters
.defaultConverter().orElseThrow())
111 .map(this::asConverterField
)
112 .forEach(repositoryFields
::add
);
114 return repositoryFields
;
117 private Optional
<FieldSpec
> loggerField(final SqlStatement sqlStatement
) {
118 return logging
.logger(ClassName
.bestGuess(sqlStatement
.getRepositoryClass()));
121 private FieldSpec
asConstantRawSqlField(final SqlStatement sqlStatement
) {
122 final var configuration
= sqlStatement
.getConfiguration();
123 final var rawStatement
= sqlStatement
.getRawStatement();
124 return fields
.prepareConstant(String
.class, constantRawSqlStatementFieldName(configuration
))
125 .initializer(fields
.initialize(rawStatement
))
126 .addJavadoc(javadoc
.fieldJavaDoc(sqlStatement
))
130 private FieldSpec
asConstantSqlField(final SqlStatement sqlStatement
) {
131 final var configuration
= sqlStatement
.getConfiguration();
132 final var rawStatement
= sqlStatement
.getRawStatement();
133 final var statement
= replaceNamedParameters(rawStatement
);
134 return fields
.prepareConstant(String
.class, constantSqlStatementFieldName(configuration
))
135 .initializer(fields
.initialize(statement
))
136 .addJavadoc(javadoc
.fieldJavaDoc(sqlStatement
))
140 private static String
replaceNamedParameters(final String rawSqlStatement
) {
141 return rawSqlStatement
.replaceAll(SqlStatementParser
.NAMED_PARAMETER_PATTERN
.pattern(), "?");
144 private FieldSpec
asConstantSqlParameterIndexField(final SqlStatement sqlStatement
) {
145 final var configuration
= sqlStatement
.getConfiguration();
146 return fields
.prepareConstant(TypicalTypes
.MAP_OF_STRING_AND_ARRAY_OF_INTS
,
147 constantSqlStatementParameterIndexFieldName(configuration
))
148 .initializer("new $T<>($L)", HashMap
.class, sqlStatement
.getConfiguration().parameters().size())
152 private FieldSpec
asConverterField(final ResultRowConverter converter
) {
154 converter
.converterTypeName().orElseThrow(), // TODO: throw business exception
155 converter
.alias().orElseThrow()); // TODO: throw business exception
159 public String
constantSqlStatementFieldName(final SqlConfiguration configuration
) {
160 return configuration
.name()
161 .map(name
-> name
.replaceAll(NAME_REGEX
, NAME_REPLACEMENT
))
162 .map(name
-> name
.toUpperCase(Locale
.ROOT
))
163 .map(name
-> name
+ vendorSuffix(configuration
))
168 public String
constantRawSqlStatementFieldName(final SqlConfiguration configuration
) {
169 return constantSqlStatementFieldName(configuration
) + names
.rawSuffix();
173 public String
constantSqlStatementParameterIndexFieldName(final SqlConfiguration configuration
) {
174 return constantSqlStatementFieldName(configuration
) + names
.indexSuffix();
177 private static String
vendorSuffix(final SqlConfiguration configuration
) {
178 return configuration
.vendor()
179 .filter(not(Strings
::isBlank
))
180 .map(vendor
-> "_" + vendor
.replace(" ", "_").toUpperCase(Locale
.ROOT
))