[#7153] Allow for configuring a locale to be used for upper/lower casing inside of jOOQ

This commit is contained in:
Lukas Eder 2018-08-20 12:11:37 +02:00
parent 0884090495
commit d4da4946f8
10 changed files with 204 additions and 26 deletions

View File

@ -0,0 +1,115 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.conf;
import static org.jooq.tools.StringUtils.defaultIfNull;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.annotation.adapters.XmlAdapter;
/**
* @author Lukas Eder
*/
public class LocaleAdapter extends XmlAdapter<String, Locale> {
private static final Pattern SIMPLE_LANGUAGE_TAG = Pattern.compile("(\\w+)(?:-(\\w+)(?:-(\\w+))?)?");
@Override
public Locale unmarshal(String v) throws Exception {
Locale result = null;
if (v == null)
return result;
result = Locale.forLanguageTag(v);
return result;
}
@Override
public String marshal(Locale v) throws Exception {
String result = null;
if (v == null)
return result;
result = v.toLanguageTag();
return result;
}
}

View File

@ -9,11 +9,13 @@
package org.jooq.conf;
import java.io.Serializable;
import java.util.Locale;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**
@ -46,6 +48,9 @@ public class Settings
@XmlElement(defaultValue = "AS_IS")
@XmlSchemaType(name = "string")
protected RenderKeywordStyle renderKeywordStyle = RenderKeywordStyle.AS_IS;
@XmlElement(type = String.class)
@XmlJavaTypeAdapter(LocaleAdapter.class)
protected Locale renderLocale;
@XmlElement(defaultValue = "false")
protected Boolean renderFormatted = false;
protected RenderFormatting renderFormatting;
@ -258,6 +263,30 @@ public class Settings
this.renderKeywordStyle = value;
}
/**
* The Locale to be used with any locale dependent logic (as e.g. transforming names to lower / uppper case).
*
* @return
* possible object is
* {@link String }
*
*/
public Locale getRenderLocale() {
return renderLocale;
}
/**
* Sets the value of the renderLocale property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setRenderLocale(Locale value) {
this.renderLocale = value;
}
/**
* Whether rendered SQL should be pretty-printed.
*
@ -1160,6 +1189,11 @@ public class Settings
return this;
}
public Settings withRenderLocale(Locale value) {
setRenderLocale(value);
return this;
}
public Settings withRenderFormatted(Boolean value) {
setRenderFormatted(value);
return this;
@ -1363,6 +1397,11 @@ public class Settings
sb.append(renderKeywordStyle);
sb.append("</renderKeywordStyle>");
}
if (renderLocale!= null) {
sb.append("<renderLocale>");
sb.append(renderLocale);
sb.append("</renderLocale>");
}
if (renderFormatted!= null) {
sb.append("<renderFormatted>");
sb.append(renderFormatted);
@ -1598,6 +1637,15 @@ public class Settings
return false;
}
}
if (renderLocale == null) {
if (other.renderLocale!= null) {
return false;
}
} else {
if (!renderLocale.equals(other.renderLocale)) {
return false;
}
}
if (renderFormatted == null) {
if (other.renderFormatted!= null) {
return false;
@ -1925,6 +1973,7 @@ public class Settings
result = ((prime*result)+((renderMapping == null)? 0 :renderMapping.hashCode()));
result = ((prime*result)+((renderNameStyle == null)? 0 :renderNameStyle.hashCode()));
result = ((prime*result)+((renderKeywordStyle == null)? 0 :renderKeywordStyle.hashCode()));
result = ((prime*result)+((renderLocale == null)? 0 :renderLocale.hashCode()));
result = ((prime*result)+((renderFormatted == null)? 0 :renderFormatted.hashCode()));
result = ((prime*result)+((renderFormatting == null)? 0 :renderFormatting.hashCode()));
result = ((prime*result)+((renderScalarSubqueriesForStoredFunctions == null)? 0 :renderScalarSubqueriesForStoredFunctions.hashCode()));

View File

@ -47,6 +47,7 @@ import java.io.File;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.Locale;
/**
* Convenience methods for jOOQ runtime settings.
@ -161,6 +162,14 @@ public final class SettingsTools {
return defaultIfNull(settings.isReflectionCaching(), true);
}
/**
* The render locale that is applicable, or the default locale if no such
* locale is configured.
*/
public static final Locale renderLocale(Settings settings) {
return defaultIfNull(settings.getRenderLocale(), Locale.getDefault());
}
/**
* Lazy access to {@link RenderMapping}.
*/

View File

@ -41,6 +41,7 @@ import static java.lang.Math.max;
import static java.lang.Math.min;
import static org.jooq.XMLFormat.RecordFormat.COLUMN_NAME_ELEMENTS;
import static org.jooq.XMLFormat.RecordFormat.VALUE_ELEMENTS_WITH_FIELD_ATTRIBUTE;
import static org.jooq.conf.SettingsTools.renderLocale;
import static org.jooq.impl.DSL.insertInto;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.table;
@ -673,7 +674,7 @@ abstract class AbstractCursor<R extends Record> implements Formattable, Iterable
if (format.format())
writer.append(' ');
JSONValue.writeJSONString(field.getDataType().getTypeName().toUpperCase(), writer);
JSONValue.writeJSONString(field.getDataType().getTypeName().toUpperCase(renderLocale(configuration.settings())), writer);
if (format.format())
writer.append(format.newline()).append(format.indentString(2));
@ -886,7 +887,7 @@ abstract class AbstractCursor<R extends Record> implements Formattable, Iterable
writer.append(escapeXML(field.getName()));
writer.append("\"");
writer.append(" type=\"");
writer.append(field.getDataType().getTypeName().toUpperCase());
writer.append(field.getDataType().getTypeName().toUpperCase(renderLocale(configuration.settings())));
writer.append("\"/>");
}
@ -1310,7 +1311,7 @@ abstract class AbstractCursor<R extends Record> implements Formattable, Iterable
}
eField.setAttribute("name", field.getName());
eField.setAttribute("type", field.getDataType().getTypeName().toUpperCase());
eField.setAttribute("type", field.getDataType().getTypeName().toUpperCase(renderLocale(configuration.settings())));
eFields.appendChild(eField);
}
@ -1386,7 +1387,7 @@ abstract class AbstractCursor<R extends Record> implements Formattable, Iterable
}
attrs.addAttribute("", "", "name", "CDATA", field.getName());
attrs.addAttribute("", "", "type", "CDATA", field.getDataType().getTypeName().toUpperCase());
attrs.addAttribute("", "", "type", "CDATA", field.getDataType().getTypeName().toUpperCase(renderLocale(configuration.settings())));
handler.startElement("", "", "field", attrs);
handler.endElement("", "", "field");

View File

@ -47,6 +47,7 @@ import static org.jooq.SQLDialect.HSQLDB;
// ...
import static org.jooq.conf.RenderNameStyle.LOWER;
import static org.jooq.conf.RenderNameStyle.UPPER;
import static org.jooq.conf.SettingsTools.renderLocale;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.unquotedName;
import static org.jooq.impl.Keywords.K_BEGIN;
@ -589,9 +590,9 @@ abstract class AbstractDMLQuery<R extends Record> extends AbstractQuery {
// and wants to query HSQLDB (default to upper case), they may choose
// to overwrite casing using RenderKeywordStyle.
if (style == UPPER)
names[i] = returningResolvedAsterisks.get(i).getName().toUpperCase();
names[i] = returningResolvedAsterisks.get(i).getName().toUpperCase(renderLocale(configuration().settings()));
else if (style == LOWER)
names[i] = returningResolvedAsterisks.get(i).getName().toLowerCase();
names[i] = returningResolvedAsterisks.get(i).getName().toLowerCase(renderLocale(configuration().settings()));
else
names[i] = returningResolvedAsterisks.get(i).getName();
}

View File

@ -37,6 +37,7 @@
*/
package org.jooq.impl;
import static org.jooq.conf.SettingsTools.renderLocale;
import static org.jooq.impl.Tools.EMPTY_INT;
import static org.jooq.impl.Tools.EMPTY_QUERY;
import static org.jooq.impl.Tools.EMPTY_STRING;
@ -555,33 +556,28 @@ class DefaultExecuteContext implements ExecuteContext {
// Analyse SQL in plain SQL queries:
else {
String s = query.getSQL().toLowerCase();
String s = query.getSQL().toLowerCase(renderLocale(configuration().settings()));
// TODO: Use a simple lexer to parse SQL here. Potentially, the
// SQL Console's SQL formatter could be used...?
if (s.matches("^(with\\b.*?\\bselect|select|explain)\\b.*?")) {
if (s.matches("^(with\\b.*?\\bselect|select|explain)\\b.*?"))
return ExecuteType.READ;
}
// These are sample DML statements. There may be many more
else if (s.matches("^(insert|update|delete|merge|replace|upsert|lock)\\b.*?")) {
else if (s.matches("^(insert|update|delete|merge|replace|upsert|lock)\\b.*?"))
return ExecuteType.WRITE;
}
// These are only sample DDL statements. There may be many more
else if (s.matches("^(create|alter|drop|truncate|grant|revoke|analyze|comment|flashback|enable|disable)\\b.*?")) {
else if (s.matches("^(create|alter|drop|truncate|grant|revoke|analyze|comment|flashback|enable|disable)\\b.*?"))
return ExecuteType.DDL;
}
// JDBC escape syntax for routines
else if (s.matches("^\\s*\\{\\s*(\\?\\s*=\\s*)call.*?")) {
else if (s.matches("^\\s*\\{\\s*(\\?\\s*=\\s*)call.*?"))
return ExecuteType.ROUTINE;
}
// Vendor-specific calling of routines / procedural blocks
else if (s.matches("^(call|begin|declare)\\b.*?")) {
else if (s.matches("^(call|begin|declare)\\b.*?"))
return ExecuteType.ROUTINE;
}
}
}

View File

@ -43,6 +43,7 @@ import static org.jooq.conf.ParamType.INLINED;
import static org.jooq.conf.ParamType.NAMED;
import static org.jooq.conf.RenderNameStyle.LOWER;
import static org.jooq.conf.RenderNameStyle.UPPER;
import static org.jooq.conf.SettingsTools.renderLocale;
import static org.jooq.impl.Identifiers.QUOTES;
import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER;
import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER_ESCAPED;
@ -459,7 +460,7 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
||
// [#2367] ... yet, do quote when an identifier is a SQLite keyword
(family == SQLITE && SQLITE_KEYWORDS.contains(literal.toUpperCase()))
(family == SQLITE && SQLITE_KEYWORDS.contains(literal.toUpperCase(renderLocale(configuration().settings()))))
||
@ -468,9 +469,9 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
if (!needsQuote) {
if (LOWER == cachedRenderNameStyle)
literal = literal.toLowerCase();
literal = literal.toLowerCase(renderLocale(configuration().settings()));
else if (UPPER == cachedRenderNameStyle)
literal = literal.toUpperCase();
literal = literal.toUpperCase(renderLocale(configuration().settings()));
sql(literal, true);
}

View File

@ -3349,15 +3349,12 @@ final class Tools {
static final String getPropertyName(String methodName) {
String name = methodName;
if (name.startsWith("is") && name.length() > 2) {
if (name.startsWith("is") && name.length() > 2)
name = name.substring(2, 3).toLowerCase() + name.substring(3);
}
else if (name.startsWith("get") && name.length() > 3) {
else if (name.startsWith("get") && name.length() > 3)
name = name.substring(3, 4).toLowerCase() + name.substring(4);
}
else if (name.startsWith("set") && name.length() > 3) {
else if (name.startsWith("set") && name.length() > 3)
name = name.substring(3, 4).toLowerCase() + name.substring(4);
}
return name;
}

View File

@ -20,6 +20,11 @@
<xjc:javaType name="java.util.regex.Pattern" adapter="org.jooq.conf.RegexAdapter"/>
</jaxb:bindings>
<!-- [#7153] Locales -->
<jaxb:bindings schemaLocation="../../xsd/jooq-runtime-3.12.0.xsd" multiple="true" node="//xs:element[@name='renderLocale']">
<xjc:javaType name="java.util.Locale" adapter="org.jooq.conf.LocaleAdapter"/>
</jaxb:bindings>
<!-- Annotate the following classes with @SuppressWarnings -->
<jaxb:bindings schemaLocation="../../xsd/jooq-runtime-3.12.0.xsd" multiple="true" node="//xs:complexType">
<inheritance:extends>org.jooq.conf.SettingsBase</inheritance:extends>

View File

@ -44,6 +44,10 @@ This is set to "QUOTED" by default for backwards-compatibility]]></jxb:javadoc><
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether SQL keywords should be rendered with upper or lower case.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="renderLocale" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The Locale to be used with any locale dependent logic (as e.g. transforming names to lower / uppper case).]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="renderFormatted" type="boolean" minOccurs="0" maxOccurs="1" default="false">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[Whether rendered SQL should be pretty-printed.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>