- [#5877] Add <enumConverter/> flag in <forcedType/> to auto-generate EnumConverter - [#5884] Allow for specifying Java expressions as Converter / Binding configurations
This commit is contained in:
parent
5f72c6080f
commit
c52864cbf5
@ -39,9 +39,12 @@ import static java.util.Collections.unmodifiableSet;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.util.AbstractGenerator.Language.JAVA;
|
||||
import static org.jooq.util.AbstractGenerator.Language.SCALA;
|
||||
import static org.jooq.util.GenerationUtil.ExpressionType.CONSTRUCTOR_REFERENCE;
|
||||
import static org.jooq.util.GenerationUtil.ExpressionType.EXPRESSION;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jooq.Name;
|
||||
import org.jooq.SQLDialect;
|
||||
@ -55,6 +58,9 @@ import org.jooq.util.h2.H2DataType;
|
||||
*/
|
||||
class GenerationUtil {
|
||||
|
||||
static final Pattern TYPE_REFERENCE_PATTERN = Pattern.compile("^((?:[\\p{L}_$][\\p{L}\\p{N}_$]*\\.)*[\\p{L}_$][\\p{L}\\p{N}_$]*)((?:<.*>|\\[.*\\])*)$");
|
||||
static final Pattern PLAIN_GENERIC_TYPE_PATTERN = Pattern.compile("[<\\[]((?:[\\p{L}_$][\\p{L}\\p{N}_$]*\\.)*[\\p{L}_$][\\p{L}\\p{N}_$]*)[>\\]]");
|
||||
|
||||
private static Set<String> JAVA_KEYWORDS = unmodifiableSet(new HashSet<String>(asList(
|
||||
"abstract",
|
||||
"assert",
|
||||
@ -414,4 +420,16 @@ class GenerationUtil {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ExpressionType expressionType(String expression) {
|
||||
if (TYPE_REFERENCE_PATTERN.matcher(expression).matches())
|
||||
return CONSTRUCTOR_REFERENCE;
|
||||
else
|
||||
return EXPRESSION;
|
||||
}
|
||||
|
||||
enum ExpressionType {
|
||||
CONSTRUCTOR_REFERENCE,
|
||||
EXPRESSION
|
||||
}
|
||||
}
|
||||
|
||||
@ -1660,19 +1660,17 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
final String attrId = out.ref(getStrategy().getJavaIdentifier(attribute), 2);
|
||||
final String attrName = attribute.getName();
|
||||
final String attrComment = StringUtils.defaultString(attribute.getComment());
|
||||
final List<String> converters = out.ref(list(
|
||||
attribute.getType().getConverter(),
|
||||
attribute.getType().getBinding()
|
||||
));
|
||||
final List<String> converter = out.ref(list(attribute.getType().getConverter()));
|
||||
final List<String> binding = out.ref(list(attribute.getType().getBinding()));
|
||||
|
||||
if (scala) {
|
||||
out.tab(1).println("private val %s : %s[%s, %s] = %s.createField(\"%s\", %s, this, \"%s\"[[before=, ][new %s]])",
|
||||
attrId, UDTField.class, recordType, attrType, UDTImpl.class, attrName, attrTypeRef, escapeString(""), converters);
|
||||
out.tab(1).println("private val %s : %s[%s, %s] = %s.createField(\"%s\", %s, this, \"%s\"" + converterTemplate(converter) + converterTemplate(binding) + ")",
|
||||
attrId, UDTField.class, recordType, attrType, UDTImpl.class, attrName, attrTypeRef, escapeString(""), converter, binding);
|
||||
}
|
||||
else {
|
||||
out.tab(1).javadoc("The attribute <code>%s</code>.%s", attribute.getQualifiedOutputName(), defaultIfBlank(" " + attrComment, ""));
|
||||
out.tab(1).println("public static final %s<%s, %s> %s = createField(\"%s\", %s, %s, \"%s\"[[before=, ][new %s()]]);",
|
||||
UDTField.class, recordType, attrType, attrId, attrName, attrTypeRef, udtId, escapeString(""), converters);
|
||||
out.tab(1).println("public static final %s<%s, %s> %s = createField(\"%s\", %s, %s, \"%s\"" + converterTemplate(converter) + converterTemplate(binding) + ");",
|
||||
UDTField.class, recordType, attrType, attrId, attrName, attrTypeRef, udtId, escapeString(""), converter, binding);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1995,8 +1993,6 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -3325,23 +3321,21 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
final String columnId = out.ref(getStrategy().getJavaIdentifier(column), colRefSegments(column));
|
||||
final String columnName = column.getName();
|
||||
final String columnComment = StringUtils.defaultString(column.getComment());
|
||||
final List<String> converters = out.ref(list(
|
||||
column.getType().getConverter(),
|
||||
column.getType().getBinding()
|
||||
));
|
||||
final List<String> converter = out.ref(list(column.getType().getConverter()));
|
||||
final List<String> binding = out.ref(list(column.getType().getBinding()));
|
||||
|
||||
out.tab(1).javadoc("The column <code>%s</code>.%s", column.getQualifiedOutputName(), defaultIfBlank(" " + escapeEntities(columnComment), ""));
|
||||
|
||||
if (scala) {
|
||||
out.tab(1).println("val %s : %s[%s, %s] = createField(\"%s\", %s, \"%s\"[[before=, ][new %s()]])",
|
||||
columnId, TableField.class, recordType, columnType, columnName, columnTypeRef, escapeString(columnComment), converters);
|
||||
out.tab(1).println("val %s : %s[%s, %s] = createField(\"%s\", %s, \"%s\"" + converterTemplate(converter) + converterTemplate(binding) + ")",
|
||||
columnId, TableField.class, recordType, columnType, columnName, columnTypeRef, escapeString(columnComment), converter, binding);
|
||||
}
|
||||
else {
|
||||
String isStatic = generateInstanceFields() ? "" : "static ";
|
||||
String tableRef = generateInstanceFields() ? "this" : out.ref(getStrategy().getJavaIdentifier(table), 2);
|
||||
|
||||
out.tab(1).println("public %sfinal %s<%s, %s> %s = createField(\"%s\", %s, %s, \"%s\"[[before=, ][new %s()]]);",
|
||||
isStatic, TableField.class, recordType, columnType, columnId, columnName, columnTypeRef, tableRef, escapeString(columnComment), converters);
|
||||
out.tab(1).println("public %sfinal %s<%s, %s> %s = createField(\"%s\", %s, %s, \"%s\"" + converterTemplate(converter) + converterTemplate(binding) + ");",
|
||||
isStatic, TableField.class, recordType, columnType, columnId, columnName, columnTypeRef, tableRef, escapeString(columnComment), converter, binding);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3687,6 +3681,21 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
closeJavaWriter(out);
|
||||
}
|
||||
|
||||
private String converterTemplate(List<String> converter) {
|
||||
if (converter == null || converter.isEmpty())
|
||||
return "[[]]";
|
||||
if (converter.size() > 1)
|
||||
throw new IllegalArgumentException();
|
||||
switch (GenerationUtil.expressionType(converter.get(0))) {
|
||||
case CONSTRUCTOR_REFERENCE:
|
||||
return "[[before=, ][new %s()]]";
|
||||
case EXPRESSION:
|
||||
return "[[before=, ][%s]]";
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
private String escapeString(String comment) {
|
||||
|
||||
if (comment == null)
|
||||
@ -4172,17 +4181,18 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
final String returnType = (routine.getReturnValue() == null)
|
||||
? Void.class.getName()
|
||||
: out.ref(getJavaType(routine.getReturnType()));
|
||||
final List<?> returnTypeRef = list((routine.getReturnValue() != null)
|
||||
final List<String> returnTypeRef = list((routine.getReturnValue() != null)
|
||||
? getJavaTypeReference(database, routine.getReturnType())
|
||||
: null);
|
||||
final List<?> returnConverterType = out.ref(list(
|
||||
final List<String> returnConverter = out.ref(list(
|
||||
(routine.getReturnValue() != null)
|
||||
? routine.getReturnType().getConverter()
|
||||
: null,
|
||||
: null));
|
||||
final List<String> returnBinding = out.ref(list(
|
||||
(routine.getReturnValue() != null)
|
||||
? routine.getReturnType().getBinding()
|
||||
: null
|
||||
));
|
||||
: null));
|
||||
|
||||
final List<String> interfaces = out.ref(getStrategy().getJavaClassImplements(routine, Mode.DEFAULT));
|
||||
final String schemaId = out.ref(getStrategy().getFullJavaIdentifier(schema), 2);
|
||||
final List<String> packageId = out.ref(getStrategy().getFullJavaIdentifiers(routine.getPackage()), 2);
|
||||
@ -4218,8 +4228,8 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
printClassAnnotations(out, schema);
|
||||
|
||||
if (scala) {
|
||||
out.println("class %s extends %s[%s](\"%s\", %s[[before=, ][%s]][[before=, ][%s]][[before=, ][new %s()]])[[before= with ][separator= with ][%s]] {",
|
||||
className, AbstractRoutine.class, returnType, routine.getName(), schemaId, packageId, returnTypeRef, returnConverterType, interfaces);
|
||||
out.println("class %s extends %s[%s](\"%s\", %s[[before=, ][%s]][[before=, ][%s]]" + converterTemplate(returnConverter) + converterTemplate(returnBinding) + ")[[before= with ][separator= with ][%s]] {",
|
||||
className, AbstractRoutine.class, returnType, routine.getName(), schemaId, packageId, returnTypeRef, returnConverter, returnBinding, interfaces);
|
||||
}
|
||||
else {
|
||||
out.println("public class %s extends %s<%s>[[before= implements ][%s]] {",
|
||||
@ -4234,15 +4244,13 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
final String paramComment = StringUtils.defaultString(parameter.getComment());
|
||||
final String isDefaulted = parameter.isDefaulted() ? "true" : "false";
|
||||
final String isUnnamed = parameter.isUnnamed() ? "true" : "false";
|
||||
final List<String> converters = out.ref(list(
|
||||
parameter.getType().getConverter(),
|
||||
parameter.getType().getBinding()
|
||||
));
|
||||
final List<String> converter = out.ref(list(parameter.getType().getConverter()));
|
||||
final List<String> binding = out.ref(list(parameter.getType().getBinding()));
|
||||
|
||||
out.tab(1).javadoc("The parameter <code>%s</code>.%s", parameter.getQualifiedOutputName(), defaultIfBlank(" " + paramComment, ""));
|
||||
|
||||
out.tab(1).println("public static final %s<%s> %s = createParameter(\"%s\", %s, %s, %s[[before=, ][new %s()]]);",
|
||||
Parameter.class, paramType, paramId, paramName, paramTypeRef, isDefaulted, isUnnamed, converters);
|
||||
out.tab(1).println("public static final %s<%s> %s = createParameter(\"%s\", %s, %s, %s" + converterTemplate(returnConverter) + converterTemplate(returnBinding) + ");",
|
||||
Parameter.class, paramType, paramId, paramName, paramTypeRef, isDefaulted, isUnnamed, converter, binding);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4253,7 +4261,7 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
else {
|
||||
out.tab(1).javadoc("Create a new routine call instance");
|
||||
out.tab(1).println("public %s() {", className);
|
||||
out.tab(2).println("super(\"%s\", %s[[before=, ][%s]][[before=, ][%s]][[before=, ][new %s()]]);", routine.getName(), schemaId, packageId, returnTypeRef, returnConverterType);
|
||||
out.tab(2).println("super(\"%s\", %s[[before=, ][%s]][[before=, ][%s]]" + converterTemplate(returnConverter) + converterTemplate(returnBinding) + ");", routine.getName(), schemaId, packageId, returnTypeRef, returnConverter, returnBinding);
|
||||
|
||||
|
||||
if (routine.getAllParameters().size() > 0) {
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
package org.jooq.util;
|
||||
|
||||
import static org.jooq.util.GenerationUtil.PLAIN_GENERIC_TYPE_PATTERN;
|
||||
import static org.jooq.util.GenerationUtil.TYPE_REFERENCE_PATTERN;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
@ -33,8 +36,6 @@ public class JavaWriter extends GeneratorWriter<JavaWriter> {
|
||||
private final String className;
|
||||
private final boolean isJava;
|
||||
private final boolean isScala;
|
||||
private final Pattern REF_PATTERN = Pattern.compile("((?:[\\p{L}_$][\\p{L}\\p{N}_$]*\\.)*[\\p{L}_$][\\p{L}\\p{N}_$]*)((?:<.*>|\\[.*\\])*)");
|
||||
private final Pattern PLAIN_GENERIC_TYPE_PATTERN = Pattern.compile("[<\\[]((?:[\\p{L}_$][\\p{L}\\p{N}_$]*\\.)*[\\p{L}_$][\\p{L}\\p{N}_$]*)[>\\]]");
|
||||
|
||||
public JavaWriter(File file, String fullyQualifiedTypes) {
|
||||
this(file, fullyQualifiedTypes, null);
|
||||
@ -202,7 +203,7 @@ public class JavaWriter extends GeneratorWriter<JavaWriter> {
|
||||
|
||||
// com.example.Table.TABLE.COLUMN (with keepSegments = 3)
|
||||
if (fullyQualifiedTypes == null || !fullyQualifiedTypes.matcher(c).matches()) {
|
||||
Matcher m = REF_PATTERN.matcher(c);
|
||||
Matcher m = TYPE_REFERENCE_PATTERN.matcher(c);
|
||||
|
||||
if (m.find()) {
|
||||
|
||||
|
||||
@ -905,8 +905,10 @@ public abstract class AbstractDatabase implements Database {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(type.getBinding()) && StringUtils.isBlank(type.getConverter())) {
|
||||
log.warn("Bad configuration for <forcedType/>. Either <binding/> or <converter/> is required: " + toString(type));
|
||||
if (StringUtils.isBlank(type.getBinding()) &&
|
||||
StringUtils.isBlank(type.getConverter()) &&
|
||||
!Boolean.TRUE.equals(type.isEnumConverter())) {
|
||||
log.warn("Bad configuration for <forcedType/>. Either <binding/> or <converter/> or <enumConverter/> is required: " + toString(type));
|
||||
|
||||
it2.remove();
|
||||
continue;
|
||||
@ -925,6 +927,10 @@ public abstract class AbstractDatabase implements Database {
|
||||
log.warn("Bad configuration for <forcedType/>. <converter/> is not allowed when <name/> is provided: " + toString(type));
|
||||
type.setConverter(null);
|
||||
}
|
||||
if (Boolean.TRUE.equals(type.isEnumConverter())) {
|
||||
log.warn("Bad configuration for <forcedType/>. <enumConverter/> is not allowed when <name/> is provided: " + toString(type));
|
||||
type.setEnumConverter(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (type.getUserType() != null && StringUtils.equals(type.getUserType(), typeName)) {
|
||||
|
||||
@ -52,6 +52,7 @@ import org.jooq.Name;
|
||||
import org.jooq.exception.SQLDialectNotSupportedException;
|
||||
import org.jooq.impl.DateAsTimestampBinding;
|
||||
import org.jooq.impl.DefaultDataType;
|
||||
import org.jooq.impl.EnumConverter;
|
||||
import org.jooq.impl.SQLDataType;
|
||||
import org.jooq.tools.JooqLogger;
|
||||
import org.jooq.tools.StringUtils;
|
||||
@ -137,27 +138,36 @@ abstract class AbstractTypedElementDefinition<T extends Definition>
|
||||
// [#677] Forced types for matching regular expressions
|
||||
ForcedType forcedType = db.getConfiguredForcedType(child, definedType);
|
||||
if (forcedType != null) {
|
||||
String type = forcedType.getName();
|
||||
String uType = forcedType.getName();
|
||||
String converter = null;
|
||||
String binding = result.getBinding();
|
||||
|
||||
CustomType customType = customType(db, forcedType);
|
||||
if (customType != null) {
|
||||
type = (!StringUtils.isBlank(customType.getType()))
|
||||
uType = (!StringUtils.isBlank(customType.getType()))
|
||||
? customType.getType()
|
||||
: customType.getName();
|
||||
|
||||
if (!StringUtils.isBlank(customType.getConverter()))
|
||||
if (Boolean.TRUE.equals(customType.isEnumConverter())) {
|
||||
String tType = DefaultDataType
|
||||
.getDataType(db.getDialect(), definedType.getType(), definedType.getPrecision(), definedType.getScale())
|
||||
.getType()
|
||||
.getName();
|
||||
|
||||
converter = "new " + EnumConverter.class.getName() + "<" + tType + ", " + uType + ">(" + tType + ".class, " + uType + ".class)";
|
||||
}
|
||||
else if (!StringUtils.isBlank(customType.getConverter())) {
|
||||
converter = customType.getConverter();
|
||||
}
|
||||
|
||||
if (!StringUtils.isBlank(customType.getBinding()))
|
||||
binding = customType.getBinding();
|
||||
}
|
||||
|
||||
if (type != null) {
|
||||
if (uType != null) {
|
||||
log.info("Forcing type", child
|
||||
+ " with type " + definedType.getType()
|
||||
+ " into " + type
|
||||
+ " into " + uType
|
||||
+ (converter != null ? " using converter " + converter : "")
|
||||
+ (binding != null ? " using binding " + binding : ""));
|
||||
|
||||
@ -171,7 +181,7 @@ abstract class AbstractTypedElementDefinition<T extends Definition>
|
||||
int s = 0;
|
||||
|
||||
// [#2486] Allow users to override length, precision, and scale
|
||||
Matcher matcher = LENGTH_PRECISION_SCALE_PATTERN.matcher(type);
|
||||
Matcher matcher = LENGTH_PRECISION_SCALE_PATTERN.matcher(uType);
|
||||
if (matcher.find()) {
|
||||
if (!isEmpty(matcher.group(1))) {
|
||||
l = p = convert(matcher.group(1), int.class);
|
||||
@ -183,12 +193,12 @@ abstract class AbstractTypedElementDefinition<T extends Definition>
|
||||
}
|
||||
|
||||
try {
|
||||
forcedDataType = DefaultDataType.getDataType(db.getDialect(), type, p, s);
|
||||
forcedDataType = DefaultDataType.getDataType(db.getDialect(), uType, p, s);
|
||||
} catch (SQLDialectNotSupportedException ignore) {}
|
||||
|
||||
// [#677] SQLDataType matches are actual type-rewrites
|
||||
if (forcedDataType != null) {
|
||||
result = new DefaultDataTypeDefinition(db, child.getSchema(), type, l, p, s, n, d, (Name) null, converter, binding);
|
||||
result = new DefaultDataTypeDefinition(db, child.getSchema(), uType, l, p, s, n, d, (Name) null, converter, binding);
|
||||
}
|
||||
|
||||
// Other forced types are UDT's, enums, etc.
|
||||
@ -198,7 +208,7 @@ abstract class AbstractTypedElementDefinition<T extends Definition>
|
||||
s = result.getScale();
|
||||
String t = result.getType();
|
||||
Name u = result.getQualifiedUserType();
|
||||
result = new DefaultDataTypeDefinition(db, child.getSchema(), t, l, p, s, n, d, u, converter, binding, type);
|
||||
result = new DefaultDataTypeDefinition(db, child.getSchema(), t, l, p, s, n, d, u, converter, binding, uType);
|
||||
}
|
||||
|
||||
// [#4597] If we don't have a type-rewrite (forcedDataType) or a
|
||||
@ -235,6 +245,7 @@ abstract class AbstractTypedElementDefinition<T extends Definition>
|
||||
else {
|
||||
return new CustomType()
|
||||
.withBinding(forcedType.getBinding())
|
||||
.withEnumConverter(forcedType.isEnumConverter())
|
||||
.withConverter(forcedType.getConverter())
|
||||
.withName(name)
|
||||
.withType(forcedType.getUserType());
|
||||
|
||||
@ -717,8 +717,13 @@
|
||||
the "type" value will default to the "name" value. -->
|
||||
<element name="type" type="string" minOccurs="0" maxOccurs="1" />
|
||||
|
||||
<!-- A converter implementation for the custom type -->
|
||||
<element name="converter" type="string" minOccurs="0" maxOccurs="1" />
|
||||
<choice>
|
||||
<!-- A converter implementation for the custom type -->
|
||||
<element name="converter" type="string" minOccurs="0" maxOccurs="1" />
|
||||
|
||||
<!-- A converter implementation for the custom type -->
|
||||
<element name="enumConverter" type="boolean" minOccurs="0" maxOccurs="1" fixed="true" />
|
||||
</choice>
|
||||
|
||||
<!-- A binding implementation for the custom type -->
|
||||
<element name="binding" type="string" minOccurs="0" maxOccurs="1" />
|
||||
@ -746,8 +751,13 @@
|
||||
or <binding/> is required -->
|
||||
<element name="userType" type="string" minOccurs="0" maxOccurs="1" />
|
||||
|
||||
<!-- A converter implementation for the user type. -->
|
||||
<element name="converter" type="string" minOccurs="0" maxOccurs="1" />
|
||||
<choice>
|
||||
<!-- A converter implementation for the custom type -->
|
||||
<element name="converter" type="string" minOccurs="0" maxOccurs="1" />
|
||||
|
||||
<!-- A converter implementation for the custom type -->
|
||||
<element name="enumConverter" type="boolean" minOccurs="0" maxOccurs="1" />
|
||||
</choice>
|
||||
|
||||
<!-- A binding implementation for the custom type. -->
|
||||
<element name="binding" type="string" minOccurs="0" maxOccurs="1" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user