[#7545] Add <includeExpression> and <excludeExpression> to <forcedType>

Add <includeExpression> and <excludeExpression> as well as
<includeTypes> and <excludeTypes> to <forcedType>. The generator will
warn about usage conflicting with the legacy <expression>,
<expressions>, and <types> elements.
This commit is contained in:
Knut Wannheden 2019-04-16 15:34:24 +02:00
parent 840f5a2aed
commit adcb72fcfb
5 changed files with 289 additions and 39 deletions

View File

@ -1089,9 +1089,21 @@ public abstract class AbstractDatabase implements Database {
ForcedType type = it2.next();
if (type.getExpressions() != null) {
type.setExpression(type.getExpressions());
type.setIncludeExpression(type.getExpressions());
type.setExpressions(null);
log.warn("DEPRECATED", "The <expressions/> element in <forcedType/> is deprecated. Use <expression/> instead: " + type);
log.warn("DEPRECATED", "The <expressions/> element in <forcedType/> is deprecated. Use <includeExpression/> instead: " + type);
}
if (type.getExpression() != null) {
type.setIncludeExpression(type.getExpression());
type.setExpression(null);
log.warn("DEPRECATED", "The <expression/> element in <forcedType/> is deprecated. Use <includeExpression/> instead: " + type);
}
if (type.getTypes() != null) {
type.setIncludeTypes(type.getTypes());
type.setTypes(null);
log.warn("DEPRECATED", "The <types/> element in <forcedType/> is deprecated. Use <includeTypes/> instead: " + type);
}
if (StringUtils.isBlank(type.getName())) {
@ -1480,8 +1492,10 @@ public abstract class AbstractDatabase implements Database {
// [#5885] Only the first matching <forcedType/> is applied to the data type definition.
forcedTypeLoop:
for (ForcedType forcedType : getConfiguredForcedTypes()) {
String expression = StringUtils.defaultIfNull(forcedType.getExpressions(), forcedType.getExpression());
String types = forcedType.getTypes();
String excludeExpression = forcedType.getExcludeExpression();
String includeExpression = StringUtils.firstNonNull(forcedType.getIncludeExpression(), forcedType.getExpression(), forcedType.getExpressions());
String excludeTypes = forcedType.getExcludeTypes();
String includeTypes = StringUtils.firstNonNull(forcedType.getIncludeTypes(), forcedType.getTypes());
Nullability nullability = forcedType.getNullability();
ForcedTypeObjectType objectType = forcedType.getObjectType();
String sql = forcedType.getSql();
@ -1499,30 +1513,18 @@ public abstract class AbstractDatabase implements Database {
|| (nullability == Nullability.NULL && !definedType.isNullable())))
continue forcedTypeLoop;
if (expression != null)
if (!matches(patterns.pattern(expression), definition))
continue forcedTypeLoop;
if ( (excludeExpression != null || includeExpression != null)
&& filterExcludeInclude(
Collections.singletonList(definition),
new String[] { excludeExpression },
new String[] { includeExpression != null ? includeExpression : ".*" },
getFilters()
).isEmpty())
continue forcedTypeLoop;
if (types != null && definedType != null) {
Pattern p = patterns.pattern(types);
if ( ( !p.matcher(definedType.getType()).matches() )
&& ( definedType.getLength() == 0
|| !p.matcher(definedType.getType() + "(" + definedType.getLength() + ")").matches())
&& ( definedType.getScale() != 0
|| !p.matcher(definedType.getType() + "(" + definedType.getPrecision() + ")").matches())
&& ( !p.matcher(definedType.getType() + "(" + definedType.getPrecision() + "," + definedType.getScale() + ")").matches() )
&& ( !p.matcher(definedType.getType() + "(" + definedType.getPrecision() + ", " + definedType.getScale() + ")").matches() )
// [#5872] We should match user-defined types as well, in case of which the type might be reported
// as USER-DEFINED (in PostgreSQL)
&& ( StringUtils.isBlank(definedType.getUserType())
|| !p.matcher(definedType.getUserType()).matches()
&& !p.matcher(definedType.getQualifiedUserType().unquotedName().toString()).matches() )
) {
continue forcedTypeLoop;
}
}
if ( (definedType != null && (excludeTypes != null || includeTypes != null))
&& !typeMatchesExcludeInclude(definedType, excludeTypes, includeTypes))
continue forcedTypeLoop;
if (sql != null)
if (!matches(statements.fetchSet(sql), definition))
@ -1534,6 +1536,30 @@ public abstract class AbstractDatabase implements Database {
return null;
}
private boolean typeMatchesExcludeInclude(DataTypeDefinition type, String exclude, String include) {
if (exclude != null && matches(type, patterns.pattern(exclude)))
return false;
return include == null || matches(type, patterns.pattern(include));
}
private boolean matches(DataTypeDefinition type, Pattern pattern) {
return ( pattern.matcher(type.getType()).matches() )
|| ( type.getLength() != 0
&& pattern.matcher(type.getType() + "(" + type.getLength() + ")").matches() )
|| ( type.getScale() == 0
&& pattern.matcher(type.getType() + "(" + type.getPrecision() + ")").matches() )
|| ( pattern.matcher(type.getType() + "(" + type.getPrecision() + "," + type.getScale() + ")").matches() )
|| ( pattern.matcher(type.getType() + "(" + type.getPrecision() + ", " + type.getScale() + ")").matches() )
// [#5872] We should match user-defined types as well, in case of which the type might be reported
// as USER-DEFINED (in PostgreSQL)
|| ( !StringUtils.isBlank(type.getUserType())
&& ( pattern.matcher(type.getUserType()).matches()
|| pattern.matcher(type.getQualifiedUserType().unquotedName().toString()).matches() )
);
}
@Override
public final void setConfiguredEmbeddables(List<Embeddable> configuredEmbeddables) {
this.configuredEmbeddables = configuredEmbeddables;

View File

@ -45,12 +45,20 @@ public class ForcedType implements Serializable
@XmlJavaTypeAdapter(StringAdapter.class)
protected String binding;
@XmlJavaTypeAdapter(StringAdapter.class)
protected String excludeExpression;
@XmlJavaTypeAdapter(StringAdapter.class)
protected String includeExpression;
@XmlJavaTypeAdapter(StringAdapter.class)
protected String expression;
@XmlJavaTypeAdapter(StringAdapter.class)
protected String expressions;
@XmlJavaTypeAdapter(StringAdapter.class)
protected String sql;
@XmlJavaTypeAdapter(StringAdapter.class)
protected String excludeTypes;
@XmlJavaTypeAdapter(StringAdapter.class)
protected String includeTypes;
@XmlJavaTypeAdapter(StringAdapter.class)
protected String types;
@XmlElement(defaultValue = "ALL")
@XmlSchemaType(name = "string")
@ -184,8 +192,58 @@ public class ForcedType implements Serializable
/**
* A Java regular expression matching columns, parameters, attributes,
* etc to be forced to have this type. If provided, both "expressions" and
* "types" must match.
* etc. which must not have this type. Excludes match before includes, i.e.
* excludes have a higher priority.
*
* @return
* possible object is
* {@link String }
*
*/
public String getExcludeExpression() {
return excludeExpression;
}
/**
* Sets the value of the excludeExpression property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setExcludeExpression(String value) {
this.excludeExpression = value;
}
/**
* A Java regular expression matching columns, parameters, attributes,
* etc. to be forced to have this type. If provided, both "includeExpression" and
* "includeTypes" must match.
*
* @return
* possible object is
* {@link String }
*
*/
public String getIncludeExpression() {
return includeExpression;
}
/**
* Sets the value of the includeExpression property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setIncludeExpression(String value) {
this.includeExpression = value;
}
/**
* The same as {@link #getIncludeExpression()}. This is kept for backwards compatibility reasons.
*
* @return
* possible object is
@ -209,7 +267,7 @@ public class ForcedType implements Serializable
}
/**
* The same as expression. This is kept for backwards compatibility reasons.
* The same as {@link #getIncludeExpression()}. This is kept for backwards compatibility reasons.
*
* @return
* possible object is
@ -256,9 +314,59 @@ public class ForcedType implements Serializable
this.sql = value;
}
/**
* A Java regular expression matching data types
* which must not have this type. Excludes match before includes, i.e.
* excludes have a higher priority.
*
* @return
* possible object is
* {@link String }
*
*/
public String getExcludeTypes() {
return excludeTypes;
}
/**
* Sets the value of the excludeTypes property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setExcludeTypes(String value) {
this.excludeTypes = value;
}
/**
* A Java regular expression matching data types to be forced to have this
* type. If provided, both "expression" and "types" must match.
* type. If provided, both "includeExpression" and "includeTypes" must match.
*
* @return
* possible object is
* {@link String }
*
*/
public String getIncludeTypes() {
return includeTypes;
}
/**
* Sets the value of the includeTypes property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setIncludeTypes(String value) {
this.includeTypes = value;
}
/**
* The same as {@link #getIncludeTypes()}. This is kept for backwards compatibility reasons.
*
* @return
* possible object is
@ -354,6 +462,16 @@ public class ForcedType implements Serializable
return this;
}
public ForcedType withExcludeExpression(String value) {
setExcludeExpression(value);
return this;
}
public ForcedType withIncludeExpression(String value) {
setIncludeExpression(value);
return this;
}
public ForcedType withExpression(String value) {
setExpression(value);
return this;
@ -369,6 +487,16 @@ public class ForcedType implements Serializable
return this;
}
public ForcedType withExcludeTypes(String value) {
setExcludeTypes(value);
return this;
}
public ForcedType withIncludeTypes(String value) {
setIncludeTypes(value);
return this;
}
public ForcedType withTypes(String value) {
setTypes(value);
return this;
@ -412,6 +540,16 @@ public class ForcedType implements Serializable
sb.append(binding);
sb.append("</binding>");
}
if ((excludeExpression!= null)&&(!"".equals(excludeExpression))) {
sb.append("<excludeExpression>");
sb.append(excludeExpression);
sb.append("</excludeExpression>");
}
if ((includeExpression!= null)&&(!"".equals(includeExpression))) {
sb.append("<includeExpression>");
sb.append(includeExpression);
sb.append("</includeExpression>");
}
if ((expression!= null)&&(!"".equals(expression))) {
sb.append("<expression>");
sb.append(expression);
@ -427,6 +565,16 @@ public class ForcedType implements Serializable
sb.append(sql);
sb.append("</sql>");
}
if ((excludeTypes!= null)&&(!"".equals(excludeTypes))) {
sb.append("<excludeTypes>");
sb.append(excludeTypes);
sb.append("</excludeTypes>");
}
if ((includeTypes!= null)&&(!"".equals(includeTypes))) {
sb.append("<includeTypes>");
sb.append(includeTypes);
sb.append("</includeTypes>");
}
if ((types!= null)&&(!"".equals(types))) {
sb.append("<types>");
sb.append(types);
@ -502,6 +650,24 @@ public class ForcedType implements Serializable
return false;
}
}
if (excludeExpression == null) {
if (other.excludeExpression!= null) {
return false;
}
} else {
if (!excludeExpression.equals(other.excludeExpression)) {
return false;
}
}
if (includeExpression == null) {
if (other.includeExpression!= null) {
return false;
}
} else {
if (!includeExpression.equals(other.includeExpression)) {
return false;
}
}
if (expression == null) {
if (other.expression!= null) {
return false;
@ -529,6 +695,24 @@ public class ForcedType implements Serializable
return false;
}
}
if (excludeTypes == null) {
if (other.excludeTypes!= null) {
return false;
}
} else {
if (!excludeTypes.equals(other.excludeTypes)) {
return false;
}
}
if (includeTypes == null) {
if (other.includeTypes!= null) {
return false;
}
} else {
if (!includeTypes.equals(other.includeTypes)) {
return false;
}
}
if (types == null) {
if (other.types!= null) {
return false;
@ -568,9 +752,13 @@ public class ForcedType implements Serializable
result = ((prime*result)+((converter == null)? 0 :converter.hashCode()));
result = ((prime*result)+((enumConverter == null)? 0 :enumConverter.hashCode()));
result = ((prime*result)+((binding == null)? 0 :binding.hashCode()));
result = ((prime*result)+((excludeExpression == null)? 0 :excludeExpression.hashCode()));
result = ((prime*result)+((includeExpression == null)? 0 :includeExpression.hashCode()));
result = ((prime*result)+((expression == null)? 0 :expression.hashCode()));
result = ((prime*result)+((expressions == null)? 0 :expressions.hashCode()));
result = ((prime*result)+((sql == null)? 0 :sql.hashCode()));
result = ((prime*result)+((excludeTypes == null)? 0 :excludeTypes.hashCode()));
result = ((prime*result)+((includeTypes == null)? 0 :includeTypes.hashCode()));
result = ((prime*result)+((types == null)? 0 :types.hashCode()));
result = ((prime*result)+((nullability == null)? 0 :nullability.hashCode()));
result = ((prime*result)+((objectType == null)? 0 :objectType.hashCode()));

View File

@ -40,7 +40,7 @@ public class MatchersEnumType implements Serializable
protected String enumImplements;
/**
* This sequence matcher applies to all unqualified or qualified enum names matched by this expression. If left empty, this matcher applies to all enums.
* This enum matcher applies to all unqualified or qualified enum names matched by this expression. If left empty, this matcher applies to all enums.
*
* @return
* possible object is

View File

@ -344,7 +344,7 @@
<annotation><appinfo><jxb:class><jxb:javadoc><![CDATA[Declarative naming strategy configuration for enum names.]]></jxb:javadoc></jxb:class></appinfo></annotation>
<all>
<element name="expression" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[This sequence matcher applies to all unqualified or qualified enum names matched by this expression. If left empty, this matcher applies to all enums.]]></jxb:javadoc></jxb:property></appinfo></annotation>
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[This enum matcher applies to all unqualified or qualified enum names matched by this expression. If left empty, this matcher applies to all enums.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="enumClass" type="tns:MatcherRule" minOccurs="0" maxOccurs="1">
@ -935,23 +935,43 @@ or {@link #getBinding()} is required]]></jxb:javadoc></jxb:property></appinfo></
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A {@link org.jooq.Binding} implementation for the custom type.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="expression" type="string" minOccurs="0" maxOccurs="1">
<element name="excludeExpression" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A Java regular expression matching columns, parameters, attributes,
etc to be forced to have this type. If provided, both "expressions" and
"types" must match.]]></jxb:javadoc></jxb:property></appinfo></annotation>
etc. which must not have this type. Excludes match before includes, i.e.
excludes have a higher priority.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="includeExpression" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A Java regular expression matching columns, parameters, attributes,
etc. to be forced to have this type. If provided, both "includeExpression" and
"includeTypes" must match.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="expression" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The same as {@link #getIncludeExpression()}. This is kept for backwards compatibility reasons.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="expressions" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The same as expression. This is kept for backwards compatibility reasons.]]></jxb:javadoc></jxb:property></appinfo></annotation>
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The same as {@link #getIncludeExpression()}. This is kept for backwards compatibility reasons.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="sql" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A SQL statement that produces a table with one column containing the matched qualified or unqualified column names.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="types" type="string" minOccurs="0" maxOccurs="1">
<element name="excludeTypes" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A Java regular expression matching data types
which must not have this type. Excludes match before includes, i.e.
excludes have a higher priority.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="includeTypes" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[A Java regular expression matching data types to be forced to have this
type. If provided, both "expression" and "types" must match.]]></jxb:javadoc></jxb:property></appinfo></annotation>
type. If provided, both "includeExpression" and "includeTypes" must match.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="types" type="string" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The same as {@link #getIncludeTypes()}. This is kept for backwards compatibility reasons.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="nullability" type="tns:Nullability" default="ALL" minOccurs="0" maxOccurs="1">

View File

@ -1302,6 +1302,22 @@ public final class StringUtils {
return object != null ? object : defaultValue;
}
/**
* <p>Returns the first non-{@code null} argument.</p>
*
* @param <T> the type of the objects
* @param objects the elements to test, may not be {@code null} but empty
* @return first non-{@code null} element in {@code objects}, otherwise {@code null}
*/
@SafeVarargs
public static <T> T firstNonNull(T... objects) {
for (T object : objects) {
if (object != null)
return object;
}
return null;
}
// -------------------------------------------------------------------------
// XXX: The following methods are not part of Apache's commons-lang library
// -------------------------------------------------------------------------