[jOOQ/jOOQ#9744] Add <locale/> to code generator <target/> specification

This commit is contained in:
Lukas Eder 2020-08-21 14:32:35 +02:00
parent 441f7ccc25
commit e58593e690
11 changed files with 131 additions and 20 deletions

View File

@ -44,6 +44,7 @@ import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;
import java.util.Set;
import org.jooq.meta.Database;
@ -1141,6 +1142,16 @@ abstract class AbstractGenerator implements Generator {
this.targetEncoding = encoding;
}
@Override
public Locale getTargetLocale() {
return strategy.getTargetLocale();
}
@Override
public void setTargetLocale(Locale targetLocale) {
strategy.setTargetLocale(targetLocale);
}
@Override
public boolean getTargetClean() {
return targetClean;

View File

@ -51,6 +51,7 @@ import static org.jooq.SQLDialect.POSTGRES;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
// ...
import org.jooq.meta.ArrayDefinition;
@ -80,6 +81,7 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
private String targetDirectory;
private String targetPackage;
private Locale targetLocale = Locale.getDefault();
private boolean instanceFields = true;
private boolean javaBeansGettersAndSetters = false;
@ -127,6 +129,16 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
this.targetPackage = packageName;
}
@Override
public Locale getTargetLocale() {
return targetLocale;
}
@Override
public void setTargetLocale(Locale targetLocale) {
this.targetLocale = targetLocale;
}
// -------------------------------------------------------------------------
// Strategy methods
// -------------------------------------------------------------------------
@ -146,7 +158,7 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
// [#6307] Some databases work with per-table namespacing for indexes, not per-schema namespacing.
// In order to have non-ambiguous identifiers, we need to include the table name.
else if (definition instanceof IndexDefinition && asList(MARIADB, MYSQL).contains(definition.getDatabase().getDialect().family()))
return ((IndexDefinition) definition).getTable().getOutputName().toUpperCase() + "_" + definition.getOutputName().toUpperCase();
return ((IndexDefinition) definition).getTable().getOutputName().toUpperCase(targetLocale) + "_" + definition.getOutputName().toUpperCase(targetLocale);
@ -156,11 +168,11 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
// [#9758] And then also for foreign keys
else if (definition instanceof ForeignKeyDefinition && asList(POSTGRES).contains(definition.getDatabase().getDialect().family()))
return ((ForeignKeyDefinition) definition).getTable().getOutputName().toUpperCase() + "__" + definition.getOutputName().toUpperCase();
return ((ForeignKeyDefinition) definition).getTable().getOutputName().toUpperCase(targetLocale) + "__" + definition.getOutputName().toUpperCase(targetLocale);
// [#10481] Embeddables have a defining name (class name) and a referencing name (identifier name, member name).
else if (definition instanceof EmbeddableDefinition)
return ((EmbeddableDefinition) definition).getReferencingOutputName().toUpperCase();
return ((EmbeddableDefinition) definition).getReferencingOutputName().toUpperCase(targetLocale);
@ -168,7 +180,7 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
else
return definition.getOutputName().toUpperCase();
return definition.getOutputName().toUpperCase(targetLocale);
}
@ -263,7 +275,7 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
// [#2032] In multi-catalog setups, the catalog name goes into the package
if (definition.getDatabase().getCatalogs().size() > 1) {
sb.append(".");
sb.append(getJavaIdentifier(definition.getCatalog()).toLowerCase());
sb.append(getJavaIdentifier(definition.getCatalog()).toLowerCase(targetLocale));
}
if (!(definition instanceof CatalogDefinition)) {
@ -271,7 +283,7 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
// [#282] In multi-schema setups, the schema name goes into the package
if (definition.getDatabase().getSchemata().size() > 1) {
sb.append(".");
sb.append(getJavaIdentifier(definition.getSchema()).toLowerCase());
sb.append(getJavaIdentifier(definition.getSchema()).toLowerCase(targetLocale));
}
if (!(definition instanceof SchemaDefinition)) {
@ -325,12 +337,12 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
private String getJavaClassName0LC(Definition definition, Mode mode) {
String result = getJavaClassName0(definition, mode);
return result.substring(0, 1).toLowerCase() + result.substring(1);
return result.substring(0, 1).toLowerCase(targetLocale) + result.substring(1);
}
private String getJavaClassName0LC(String outputName, Mode mode) {
String result = getJavaClassName0(outputName, mode);
return result.substring(0, 1).toLowerCase() + result.substring(1);
return result.substring(0, 1).toLowerCase(targetLocale) + result.substring(1);
}
private String getJavaClassName0(Definition definition, Mode mode) {
@ -373,7 +385,7 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
// [#330] [#6529] A UDT inside of a package is a PL/SQL RECORD type
if (udt.getPackage() != null)
return "packages." + getJavaIdentifier(udt.getPackage()).toLowerCase() + ".udt";
return "packages." + getJavaIdentifier(udt.getPackage()).toLowerCase(targetLocale) + ".udt";
else
return "udt";
}
@ -384,10 +396,10 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
RoutineDefinition routine = (RoutineDefinition) definition;
if (routine.getPackage() instanceof UDTDefinition) {
return "udt." + getJavaIdentifier(routine.getPackage()).toLowerCase();
return "udt." + getJavaIdentifier(routine.getPackage()).toLowerCase(targetLocale);
}
else if (routine.getPackage() != null) {
return "packages." + getJavaIdentifier(routine.getPackage()).toLowerCase();
return "packages." + getJavaIdentifier(routine.getPackage()).toLowerCase(targetLocale);
}
else {
return "routines";
@ -405,7 +417,7 @@ public class DefaultGeneratorStrategy extends AbstractGeneratorStrategy {
// [#7125] An array inside of a package is a PL/SQL TABLE type
if (array.getPackage() != null)
return "packages." + getJavaIdentifier(array.getPackage()).toLowerCase() + ".udt";
return "packages." + getJavaIdentifier(array.getPackage()).toLowerCase(targetLocale) + ".udt";
else
return "udt";
}

View File

@ -57,6 +57,7 @@ import java.sql.SQLException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import javax.sql.DataSource;
@ -85,6 +86,7 @@ import org.jooq.meta.jaxb.Strategy;
import org.jooq.meta.jaxb.Target;
// ...
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
import org.jooq.tools.jdbc.JDBCUtils;
import org.jooq.util.jaxb.tools.MiniJAXB;
@ -293,6 +295,16 @@ public class GenerationTool {
if (g.getTarget() == null)
g.setTarget(new Target());
// [#9744] The <locale/> is also needed in GenerationTool:
Locale locale = Locale.getDefault();
if (!StringUtils.isBlank(g.getTarget().getLocale()))
if (true)
locale = Locale.forLanguageTag(g.getTarget().getLocale());
else
/* [java-8] */
log.info("Locale support", "Locale support has been added for the Java 8+ distributions only");
Database database = null;
try {
@ -573,7 +585,7 @@ public class GenerationTool {
log.info("Using custom schema version provider : " + svp);
}
catch (Exception ignore) {
if (d.getSchemaVersionProvider().toLowerCase().startsWith("select")) {
if (d.getSchemaVersionProvider().toLowerCase(locale).startsWith("select")) {
svp = new SQLSchemaVersionProvider(connection, d.getSchemaVersionProvider());
log.info("Using SQL schema version provider : " + d.getSchemaVersionProvider());
}
@ -589,7 +601,7 @@ public class GenerationTool {
log.info("Using custom catalog version provider : " + cvp);
}
catch (Exception ignore) {
if (d.getCatalogVersionProvider().toLowerCase().startsWith("select")) {
if (d.getCatalogVersionProvider().toLowerCase(locale).startsWith("select")) {
cvp = new SQLCatalogVersionProvider(connection, d.getCatalogVersionProvider());
log.info("Using SQL catalog version provider : " + d.getCatalogVersionProvider());
}
@ -652,6 +664,7 @@ public class GenerationTool {
if (g.getTarget().isClean() != null)
generator.setTargetClean(g.getTarget().isClean());
generator.setTargetLocale(locale);
if (g.getGenerate().isIndexes() != null)
generator.setGenerateIndexes(g.getGenerate().isIndexes());

View File

@ -39,6 +39,7 @@
package org.jooq.codegen;
import java.io.Serializable;
import java.util.Locale;
import org.jooq.meta.Database;
import org.jooq.meta.jaxb.GeneratedAnnotationType;
@ -1028,4 +1029,14 @@ public interface Generator {
* Whether the target package should be cleaned to contain only generated code after a generation run.
*/
void setTargetClean(boolean clean);
/**
* The target locale.
*/
Locale getTargetLocale();
/**
*The target locale.
*/
void setTargetLocale(Locale targetLocale);
}

View File

@ -40,6 +40,7 @@ package org.jooq.codegen;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import org.jooq.meta.AttributeDefinition;
import org.jooq.meta.ColumnDefinition;
@ -78,6 +79,16 @@ public interface GeneratorStrategy {
*/
void setTargetPackage(String packageName);
/**
* @return Get the target locale for the current configuration
*/
Locale getTargetLocale();
/**
* Initialise the target locale
*/
void setTargetLocale(Locale targetLocale);
/**
* Whether fields are instance fields (as opposed to static fields)
*/

View File

@ -49,6 +49,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@ -110,6 +111,16 @@ class GeneratorStrategyWrapper extends AbstractGeneratorStrategy {
delegate.setTargetPackage(packageName);
}
@Override
public Locale getTargetLocale() {
return delegate.getTargetLocale();
}
@Override
public void setTargetLocale(Locale targetLocale) {
delegate.setTargetLocale(targetLocale);
}
@Override
public void setInstanceFields(boolean instanceFields) {
delegate.setInstanceFields(instanceFields);

View File

@ -647,7 +647,7 @@ public class JavaGenerator extends AbstractGenerator {
// Check if we've previously encountered a Java type of the same case-insensitive, fully-qualified name.
String name = getStrategy().getFullJavaClassName(definition);
String nameLC = name.toLowerCase();
String nameLC = name.toLowerCase(getStrategy().getTargetLocale());
String existing = included.put(nameLC, name);
if (existing == null)
@ -1454,7 +1454,7 @@ public class JavaGenerator extends AbstractGenerator {
// Instance methods ship with a SELF parameter at the first position
// [#1584] Static methods don't have that
boolean instance = routine.getInParameters().size() > 0
&& routine.getInParameters().get(0).getInputName().toUpperCase().equals("SELF");
&& routine.getInParameters().get(0).getInputName().toUpperCase(getStrategy().getTargetLocale()).equals("SELF");
try {
if (!routine.isSQLUsable()) {
@ -7908,7 +7908,7 @@ public class JavaGenerator extends AbstractGenerator {
// [#5574] While MySQL usually reports actual values, it does report
// a CURRENT_TIMESTAMP expression, inconsistently
if (d != null && d.toLowerCase().startsWith("current_timestamp"))
if (d != null && d.toLowerCase(getStrategy().getTargetLocale()).startsWith("current_timestamp"))
sb.append("org.jooq.impl.DSL.field(\"")
.append(escapeString(d))
.append("\"");

View File

@ -141,11 +141,11 @@ public class MatcherStrategy extends DefaultGeneratorStrategy {
case AS_IS:
return string;
case LOWER:
return string.toLowerCase();
return string.toLowerCase(getTargetLocale());
case LOWER_FIRST_LETTER:
return StringUtils.toLC(string);
case UPPER:
return string.toUpperCase();
return string.toUpperCase(getTargetLocale());
case UPPER_FIRST_LETTER:
return StringUtils.toUC(string);
case CAMEL:

View File

@ -38,6 +38,8 @@ public class Target implements Serializable, XMLAppendable
@XmlElement(defaultValue = "UTF-8")
@XmlJavaTypeAdapter(StringAdapter.class)
protected String encoding = "UTF-8";
@XmlJavaTypeAdapter(StringAdapter.class)
protected String locale;
@XmlElement(defaultValue = "true")
protected Boolean clean = true;
@ -95,6 +97,22 @@ public class Target implements Serializable, XMLAppendable
this.encoding = value;
}
/**
* The locale to be used with all locale specific operations.
*
*/
public String getLocale() {
return locale;
}
/**
* The locale to be used with all locale specific operations.
*
*/
public void setLocale(String value) {
this.locale = value;
}
/**
* Whether the target package should be cleaned to contain only generated code after a generation run.
*
@ -149,6 +167,15 @@ public class Target implements Serializable, XMLAppendable
return this;
}
/**
* The locale to be used with all locale specific operations.
*
*/
public Target withLocale(String value) {
setLocale(value);
return this;
}
public Target withClean(Boolean value) {
setClean(value);
return this;
@ -159,6 +186,7 @@ public class Target implements Serializable, XMLAppendable
builder.append("packageName", packageName);
builder.append("directory", directory);
builder.append("encoding", encoding);
builder.append("locale", locale);
builder.append("clean", clean);
}
@ -208,6 +236,15 @@ public class Target implements Serializable, XMLAppendable
return false;
}
}
if (locale == null) {
if (other.locale!= null) {
return false;
}
} else {
if (!locale.equals(other.locale)) {
return false;
}
}
if (clean == null) {
if (other.clean!= null) {
return false;
@ -227,6 +264,7 @@ public class Target implements Serializable, XMLAppendable
result = ((prime*result)+((packageName == null)? 0 :packageName.hashCode()));
result = ((prime*result)+((directory == null)? 0 :directory.hashCode()));
result = ((prime*result)+((encoding == null)? 0 :encoding.hashCode()));
result = ((prime*result)+((locale == null)? 0 :locale.hashCode()));
result = ((prime*result)+((clean == null)? 0 :clean.hashCode()));
return result;
}

View File

@ -1608,6 +1608,10 @@ e.g. org.jooq.generated.schema1, org.jooq.generated.schema2]]></jxb:javadoc></jx
<element name="encoding" type="string" default="UTF-8" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The file encoding to be used with all output files.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="locale" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The locale to be used with all locale specific operations.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="clean" type="boolean" default="true" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether the target package should be cleaned to contain only generated code after a generation run.]]></jxb:javadoc></jxb:property></appinfo></annotation>

View File

@ -1370,7 +1370,7 @@ public final class StringUtils {
}
/**
* Change a string's first letter to lower case
* Change a string's first letter to upper case
*/
public static String toUC(String string) {
if (string == null || string.isEmpty())