fix #123 allow to specify extra annotations on repositories/methods
[yosql.git] / yosql-codegen / src / main / java / wtf / metio / yosql / codegen / dao / DefaultFieldsGenerator.java
blob06b0b9758657973d100ddf76d0f99c17768ff2de
1 /*
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
5 * in the LICENSE file.
6 */
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;
27 import java.util.*;
28 import java.util.stream.Collectors;
29 import java.util.stream.IntStream;
31 import static java.util.function.Predicate.not;
33 /**
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;
54 this.names = names;
55 this.logging = logging;
56 this.javadoc = javadoc;
57 this.fields = fields;
60 @Override
61 public Optional<CodeBlock> staticInitializer(final List<SqlStatement> statements) {
62 final var builder = CodeBlock.builder();
63 statements.stream()
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()
84 .stream()
85 .map(IntStream::of)
86 .flatMap(IntStream::boxed)
87 .map(Object::toString)
88 .collect(Collectors.joining(", ", "new int[] { ", " }"));
91 @Override
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))
127 .build();
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))
137 .build();
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())
149 .build();
152 private FieldSpec asConverterField(final ResultRowConverter converter) {
153 return fields.field(
154 converter.converterTypeName().orElseThrow(), // TODO: throw business exception
155 converter.alias().orElseThrow()); // TODO: throw business exception
158 @Override
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))
164 .orElseThrow();
167 @Override
168 public String constantRawSqlStatementFieldName(final SqlConfiguration configuration) {
169 return constantSqlStatementFieldName(configuration) + names.rawSuffix();
172 @Override
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))
181 .orElse("");