[jOOQ/jOOQ#8972] jOOQ-meta should query dictionary views for column

existence rather than the column itself
This commit is contained in:
Lukas Eder 2019-07-23 12:35:09 +02:00
parent 83a6711412
commit 4e9a29dbd6
11 changed files with 143 additions and 943 deletions

View File

@ -68,6 +68,7 @@ import org.jooq.ExecuteContext;
import org.jooq.ExecuteListenerProvider;
import org.jooq.Name;
import org.jooq.Query;
import org.jooq.Record;
import org.jooq.SQLDialect;
import org.jooq.Schema;
import org.jooq.Table;
@ -369,59 +370,62 @@ public abstract class AbstractDatabase implements Database {
}
}
// [#8972] TODO: Implement these for all non-PostgresDatabase databases.
@Override
public TableQualifier tableQualifier() {
return null;
}
@Override
public ColumnQualifier columnQualifier() {
return null;
}
@Override
public final boolean exists(TableField<?, ?> field) {
Boolean result = existFields.get(field);
if (result == null) {
ColumnQualifier qualifier = columnQualifier();
// [#8972] In the presence of a qualifier, avoid running an error-
// triggering query, which may lead to unwanted log messages
if (qualifier == null) {
try {
create(true)
.select(field)
.from(field.getTable())
.where(falseCondition())
.fetch();
result = true;
}
catch (DataAccessException e) {
result = false;
}
}
else {
Condition condition = qualifier.columnName().eq(field.getName());
Table<?> table = field.getTable();
condition = condition.and(qualifier.tableName().eq(table.getName()));
Schema schema = table.getSchema();
if (schema != null)
condition = condition.and(qualifier.tableSchema().eq(schema.getName()));
result = create().fetchExists(qualifier.table(), condition);
}
existFields.put(field, result);
}
if (result == null)
existFields.put(field, result = exists0(field));
return result;
}
/**
* [#8972] Subclasses may override this method for a more efficient implementation.
*/
protected boolean exists0(TableField<?, ?> field) {
try {
create(true)
.select(field)
.from(field.getTable())
.where(falseCondition())
.fetch();
return true;
}
catch (DataAccessException e) {
return false;
}
}
/**
* A utility method to look up a field in a single dictionary view.
*
* @param find The field to look up
* @param in The dictionary view
* @param schemaQualifier The column in the dictionary view qualifying the schema
* @param tableQualifier The column in the dictionary view qualifying the table
* @param columnQualifier The column in the dictionary view qualifying the column
*/
protected final <R extends Record> boolean exists1(
TableField<?, ?> find,
Table<R> in,
TableField<R, String> schemaQualifier,
TableField<R, String> tableQualifier,
TableField<R, String> columnQualifier
) {
Condition condition = columnQualifier.eq(find.getName());
Table<?> table = find.getTable();
condition = condition.and(tableQualifier.eq(table.getName()));
Schema schema = table.getSchema();
if (schema != null)
condition = condition.and(schemaQualifier.eq(schema.getName()));
return create().fetchExists(in, condition);
}
@Override
public final boolean existAll(TableField<?, ?>... f) {
for (TableField<?, ?> field : f)
@ -435,41 +439,53 @@ public abstract class AbstractDatabase implements Database {
public final boolean exists(Table<?> table) {
Boolean result = existTables.get(table);
if (result == null) {
TableQualifier qualifier = tableQualifier();
// [#8972] In the presence of a qualifier, avoid running an error-
// triggering query, which may lead to unwanted log messages
if (qualifier == null) {
try {
create(true)
.selectOne()
.from(table)
.where(falseCondition())
.fetch();
result = true;
}
catch (DataAccessException e) {
result = false;
}
}
else {
Condition condition = qualifier.tableName().eq(table.getName());
Schema schema = table.getSchema();
if (schema != null)
condition = condition.and(qualifier.tableSchema().eq(schema.getName()));
result = create().fetchExists(qualifier.table(), condition);
}
existTables.put(table, result);
}
if (result == null)
existTables.put(table, result = exists0(table));
return result;
}
/**
* [#8972] Subclasses may override this method for a more efficient implementation.
*/
protected boolean exists0(Table<?> table) {
try {
create(true)
.selectOne()
.from(table)
.where(falseCondition())
.fetch();
return true;
}
catch (DataAccessException e) {
return false;
}
}
/**
* A utility method to look up a table in a single dictionary view.
*
* @param find The table to look up
* @param in The dictionary view
* @param schemaQualifier The column in the dictionary view qualifying the schema
* @param tableQualifier The column in the dictionary view qualifying the table
*/
protected final <R extends Record> boolean exists1(
Table<?> find,
Table<R> in,
TableField<R, String> schemaQualifier,
TableField<R, String> tableQualifier
) {
Condition condition = tableQualifier.eq(find.getName());
Schema schema = find.getSchema();
if (schema != null)
condition = condition.and(schemaQualifier.eq(schema.getName()));
return create().fetchExists(in, condition);
}
@Override
public final boolean existAll(Table<?>... t) {
for (Table<?> table : t)

View File

@ -1,75 +0,0 @@
/*
* 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.meta;
import org.jooq.Table;
import org.jooq.TableField;
/**
* A qualifier for columns in the dictionary views.
*
* @author Lukas Eder
*/
public interface ColumnQualifier {
/**
* The table containing column meta information.
*/
Table<?> table();
/**
* The field specifying the column table's catalog name.
*/
TableField<?, String> tableCatalog();
/**
* The field specifying the column table's schema name.
*/
TableField<?, String> tableSchema();
/**
* The field specifying the column table's table name.
*/
TableField<?, String> tableName();
/**
* The field specifying the column name.
*/
TableField<?, String> columnName();
}

View File

@ -904,18 +904,6 @@ public interface Database extends AutoCloseable {
*/
boolean tableValuedFunctions();
/**
* The table qualifier used by this database, or <code>null</code>, if this
* database doesn't specify such a qualifier.
*/
TableQualifier tableQualifier();
/**
* The column qualifier used by this database, or <code>null</code>, if this
* database doesn't specify such a qualifier.
*/
ColumnQualifier columnQualifier();
/**
* Check for the existence of a table field in the dictionary views.
*/

View File

@ -1,92 +0,0 @@
/*
* 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.meta;
import org.jooq.Table;
import org.jooq.TableField;
/**
* @author Lukas Eder
*/
public class DefaultColumnQualifier implements ColumnQualifier {
private final Table<?> table;
private final TableField<?, String> tableCatalog;
private final TableField<?, String> tableSchema;
private final TableField<?, String> tableName;
private final TableField<?, String> columnName;
public DefaultColumnQualifier(
Table<?> table,
TableField<?, String> tableCatalog,
TableField<?, String> tableSchema,
TableField<?, String> tableName,
TableField<?, String> columnName
) {
this.table = table;
this.tableCatalog = tableCatalog;
this.tableSchema = tableSchema;
this.tableName = tableName;
this.columnName = columnName;
}
@Override
public Table<?> table() {
return table;
}
@Override
public TableField<?, String> tableCatalog() {
return tableCatalog;
}
@Override
public TableField<?, String> tableSchema() {
return tableSchema;
}
@Override
public TableField<?, String> tableName() {
return tableName;
}
@Override
public TableField<?, String> columnName() {
return columnName;
}
}

View File

@ -1,84 +0,0 @@
/*
* 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.meta;
import org.jooq.Table;
import org.jooq.TableField;
/**
* @author Lukas Eder
*/
public class DefaultTableQualifier implements TableQualifier {
private final Table<?> table;
private final TableField<?, String> tableCatalog;
private final TableField<?, String> tableSchema;
private final TableField<?, String> tableName;
public DefaultTableQualifier(
Table<?> table,
TableField<?, String> tableCatalog,
TableField<?, String> tableSchema,
TableField<?, String> tableName
) {
this.table = table;
this.tableCatalog = tableCatalog;
this.tableSchema = tableSchema;
this.tableName = tableName;
}
@Override
public Table<?> table() {
return table;
}
@Override
public TableField<?, String> tableCatalog() {
return tableCatalog;
}
@Override
public TableField<?, String> tableSchema() {
return tableSchema;
}
@Override
public TableField<?, String> tableName() {
return tableName;
}
}

View File

@ -1,70 +0,0 @@
/*
* 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.meta;
import org.jooq.Table;
import org.jooq.TableField;
/**
* A qualifier for tables in the dictionary views.
*
* @author Lukas Eder
*/
public interface TableQualifier {
/**
* The table containing table meta information.
*/
Table<?> table();
/**
* The field specifying the table's catalog name.
*/
TableField<?, String> tableCatalog();
/**
* The field specifying the table's schema name.
*/
TableField<?, String> tableSchema();
/**
* The field specifying the table's table name.
*/
TableField<?, String> tableName();
}

View File

@ -37,7 +37,6 @@
*/
package org.jooq.meta.h2;
import static org.jooq.impl.DSL.falseCondition;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.name;
@ -67,7 +66,8 @@ import org.jooq.Record4;
import org.jooq.Result;
import org.jooq.SQLDialect;
import org.jooq.SortOrder;
import org.jooq.exception.DataAccessException;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.impl.DSL;
import org.jooq.meta.AbstractDatabase;
import org.jooq.meta.AbstractIndexDefinition;
@ -115,6 +115,16 @@ public class H2Database extends AbstractDatabase {
return DSL.using(getConnection(), SQLDialect.H2);
}
@Override
protected boolean exists0(TableField<?, ?> field) {
return exists1(field, COLUMNS, Columns.TABLE_SCHEMA, Columns.TABLE_NAME, Columns.COLUMN_NAME);
}
@Override
protected boolean exists0(Table<?> table) {
return exists1(table, TABLES, Tables.TABLE_SCHEMA, Tables.TABLE_NAME);
}
@Override
protected List<IndexDefinition> getIndexes0() throws SQLException {
List<IndexDefinition> result = new ArrayList<>();
@ -619,43 +629,19 @@ public class H2Database extends AbstractDatabase {
private static Boolean is1_4_198;
boolean is1_4_197() {
if (is1_4_197 == null) {
// [#5874] The COLUMNS.COLUMN_TYPE column was introduced in H2 1.4.197
try {
create(true)
.select(Columns.COLUMN_TYPE)
.from(COLUMNS)
.where(falseCondition())
.fetch();
is1_4_197 = true;
}
catch (DataAccessException e) {
is1_4_197 = false;
}
}
// [#5874] The COLUMNS.COLUMN_TYPE column was introduced in H2 1.4.197
if (is1_4_197 == null)
is1_4_197 = exists(Columns.COLUMN_TYPE);
return is1_4_197;
}
boolean is1_4_198() {
if (is1_4_198 == null) {
// [#5874] The COLUMNS.IS_VISIBLE column was introduced in H2 1.4.198
try {
create(true)
.select(Columns.IS_VISIBLE)
.from(COLUMNS)
.where(falseCondition())
.fetch();
is1_4_198 = true;
}
catch (DataAccessException e) {
is1_4_198 = false;
}
}
// [#5874] The COLUMNS.IS_VISIBLE column was introduced in H2 1.4.198
if (is1_4_198 == null)
is1_4_198 = exists(Columns.IS_VISIBLE);
return is1_4_198;
}

View File

@ -64,7 +64,8 @@ import org.jooq.Record6;
import org.jooq.Result;
import org.jooq.SQLDialect;
import org.jooq.SortOrder;
import org.jooq.exception.DataAccessException;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.impl.DSL;
import org.jooq.meta.AbstractDatabase;
import org.jooq.meta.AbstractIndexDefinition;
@ -223,17 +224,10 @@ public class MySQLDatabase extends AbstractDatabase {
}
protected boolean is8() {
if (is8 == null) {
// [#6602] The mysql.proc table got removed in MySQL 8.0
try {
create(true).fetchExists(PROC);
is8 = false;
}
catch (DataAccessException ignore) {
is8 = true;
}
}
// [#6602] The mysql.proc table got removed in MySQL 8.0
if (is8 == null)
is8 = !exists(PROC);
return is8;
}
@ -532,4 +526,14 @@ public class MySQLDatabase extends AbstractDatabase {
protected DSLContext create0() {
return DSL.using(getConnection(), SQLDialect.MYSQL);
}
@Override
protected boolean exists0(TableField<?, ?> field) {
return exists1(field, Columns.COLUMNS, Columns.TABLE_SCHEMA, Columns.TABLE_NAME, Columns.COLUMN_NAME);
}
@Override
protected boolean exists0(Table<?> table) {
return exists1(table, Tables.TABLES, Tables.TABLE_SCHEMA, Tables.TABLE_NAME);
}
}

View File

@ -95,6 +95,8 @@ import org.jooq.Result;
import org.jooq.SQLDialect;
import org.jooq.Select;
import org.jooq.SortOrder;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.DSL;
import org.jooq.impl.SQLDataType;
@ -103,17 +105,14 @@ import org.jooq.meta.AbstractIndexDefinition;
import org.jooq.meta.ArrayDefinition;
import org.jooq.meta.CatalogDefinition;
import org.jooq.meta.ColumnDefinition;
import org.jooq.meta.ColumnQualifier;
import org.jooq.meta.DataTypeDefinition;
import org.jooq.meta.DefaultCheckConstraintDefinition;
import org.jooq.meta.DefaultColumnQualifier;
import org.jooq.meta.DefaultDataTypeDefinition;
import org.jooq.meta.DefaultDomainDefinition;
import org.jooq.meta.DefaultEnumDefinition;
import org.jooq.meta.DefaultIndexColumnDefinition;
import org.jooq.meta.DefaultRelations;
import org.jooq.meta.DefaultSequenceDefinition;
import org.jooq.meta.DefaultTableQualifier;
import org.jooq.meta.DomainDefinition;
import org.jooq.meta.EnumDefinition;
import org.jooq.meta.IndexColumnDefinition;
@ -123,7 +122,6 @@ import org.jooq.meta.RoutineDefinition;
import org.jooq.meta.SchemaDefinition;
import org.jooq.meta.SequenceDefinition;
import org.jooq.meta.TableDefinition;
import org.jooq.meta.TableQualifier;
import org.jooq.meta.UDTDefinition;
import org.jooq.meta.hsqldb.HSQLDBDatabase;
import org.jooq.meta.postgres.information_schema.tables.CheckConstraints;
@ -622,7 +620,6 @@ public class PostgresDatabase extends AbstractDatabase {
return result;
}
@SuppressWarnings("unchecked")
@Override
protected List<DomainDefinition> getDomains0() throws SQLException {
List<DomainDefinition> result = new ArrayList<>();
@ -850,46 +847,34 @@ public class PostgresDatabase extends AbstractDatabase {
}
boolean is94() {
if (is94 == null) {
// [#4254] INFORMATION_SCHEMA.PARAMETERS.PARAMETER_DEFAULT was added
// in PostgreSQL 9.4 only
try {
create(true)
.select(PARAMETERS.PARAMETER_DEFAULT)
.from(PARAMETERS)
.where(falseCondition())
.fetch();
is94 = true;
}
catch (DataAccessException e) {
is94 = false;
}
}
// [#4254] INFORMATION_SCHEMA.PARAMETERS.PARAMETER_DEFAULT was added
// in PostgreSQL 9.4 only
if (is94 == null)
is94 = exists(PARAMETERS.PARAMETER_DEFAULT);
return is94;
}
boolean is11() {
if (is11 == null) {
// [#7785] pg_proc.prokind was added in PostgreSQL 11 only, and
// pg_proc.proisagg was removed, incompatibly
// [#7785] pg_proc.prokind was added in PostgreSQL 11 only, and
// pg_proc.proisagg was removed, incompatibly
if (is11 == null)
is11 = exists(PG_PROC.PROKIND);
}
return is11;
}
@Override
public TableQualifier tableQualifier() {
return new DefaultTableQualifier(TABLES, TABLES.TABLE_CATALOG, TABLES.TABLE_SCHEMA, TABLES.TABLE_NAME);
protected boolean exists0(TableField<?, ?> field) {
return exists1(field, COLUMNS, COLUMNS.TABLE_SCHEMA, COLUMNS.TABLE_NAME, COLUMNS.COLUMN_NAME);
}
@Override
public ColumnQualifier columnQualifier() {
return new DefaultColumnQualifier(COLUMNS, COLUMNS.TABLE_CATALOG, COLUMNS.TABLE_SCHEMA, COLUMNS.TABLE_NAME, COLUMNS.COLUMN_NAME);
protected boolean exists0(Table<?> table) {
return exists1(table, TABLES, TABLES.TABLE_SCHEMA, TABLES.TABLE_NAME);
}
boolean canCombineArrays() {
@ -929,18 +914,11 @@ public class PostgresDatabase extends AbstractDatabase {
}
boolean canUseRoutines() {
if (canUseRoutines == null) {
// [#7892] The information_schema.routines table is not available in all PostgreSQL
// style databases, e.g. CockroachDB
try {
create(true).fetchExists(ROUTINES);
canUseRoutines = true;
}
catch (DataAccessException e) {
canUseRoutines = false;
}
}
// [#7892] The information_schema.routines table is not available in all PostgreSQL
// style databases, e.g. CockroachDB
if (canUseRoutines == null)
canUseRoutines = exists(ROUTINES);
return canUseRoutines;
}

View File

@ -1,335 +0,0 @@
/*
* 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 java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.jooq.exception.ConfigurationException;
import org.jooq.tools.Convert;
import org.jooq.tools.reflect.Reflect;
import org.jooq.tools.reflect.ReflectException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* This class allows for mashalling / unmarshalling XML content to jOOQ
* configuration objects.
* <p>
* With jOOQ 3.12, the JAXB dependency has been removed in favour of this home
* grown solution. Due to the modularisation that happened with JDK 9+ and the
* removal of JAXB from the JDK 11+, it is unreasonable to leave the burden of
* properly configuring transitive JAXB dependency to jOOQ users.
*
* @author Lukas Eder
*/
public class MiniJAXB {
public static String marshal(Object object) {
StringWriter writer = new StringWriter();
marshal(object, writer);
return writer.toString();
}
public static void marshal(Object object, OutputStream out) {
marshal(object, new OutputStreamWriter(out));
}
public static void marshal(Object object, Writer out) {
try {
XmlRootElement e = object.getClass().getAnnotation(XmlRootElement.class);
if (e != null)
out.write("<" + e.name() + ">");
out.write(object.toString());
if (e != null)
out.write("</" + e.name() + ">");
}
catch (Exception e) {
throw new ConfigurationException("Cannot print object", e);
}
}
public static <T> T unmarshal(InputStream in, Class<T> type) {
return unmarshal0(new InputSource(in), type);
}
public static <T> T unmarshal(String xml, Class<T> type) {
return unmarshal0(new InputSource(new StringReader(xml)), type);
}
public static <T> T unmarshal(File xml, Class<T> type) {
try {
return unmarshal0(new InputSource(new FileInputStream(xml)), type);
}
catch (Exception e) {
throw new ConfigurationException("Error while opening file", e);
}
}
private static <T> T unmarshal0(InputSource in, Class<T> type) {
try {
Document document = builder().parse(in);
T result = Reflect.on(type).create().get();
unmarshal0(result, document.getDocumentElement());
return result;
}
catch (Exception e) {
throw new ConfigurationException("Error while reading xml", e);
}
}
private static void unmarshal0(Object result, Element element) throws Exception {
if (result == null)
return;
Class<?> type = result.getClass();
for (Field child : type.getDeclaredFields()) {
int modifiers = child.getModifiers();
if (Modifier.isFinal(modifiers) ||
Modifier.isStatic(modifiers))
continue;
XmlElementWrapper w = child.getAnnotation(XmlElementWrapper.class);
XmlElement e = child.getAnnotation(XmlElement.class);
XmlJavaTypeAdapter a = child.getAnnotation(XmlJavaTypeAdapter.class);
String childName = child.getName();
String childElementName =
w != null
? "##default".equals(w.name())
? child.getName()
: w.name()
: e == null || "##default".equals(e.name())
? childName
: e.name();
Element childElement = child(element, childElementName);
if (childElement == null)
continue;
Class<?> childType = child.getType();
if (List.class.isAssignableFrom(childType) && w != null && e != null) {
List<Object> list = new ArrayList<Object>();
unmarshalList0(list, childElement, e.name(), (Class<?>) ((ParameterizedType) child.getGenericType()).getActualTypeArguments()[0]);
Reflect.on(result).set(childName, list);
}
else if (childType.getAnnotation(XmlEnum.class) != null) {
Reflect.on(result).set(childName, Convert.convert(childElement.getTextContent().trim(), childType));
}
else if (childType.getAnnotation(XmlType.class) != null) {
Object object = Reflect.on(childType).create().get();
Reflect.on(result).set(childName, object);
unmarshal0(object, childElement);
}
else if (a != null) {
@SuppressWarnings("unchecked")
XmlAdapter<Object, Object> adapter = a.value().getConstructor().newInstance();
Reflect.on(result).set(childName, adapter.unmarshal(childElement.getTextContent().trim()));
}
else {
Reflect.on(result).set(childName, Convert.convert(childElement.getTextContent().trim(), childType));
}
}
}
private static void unmarshalList0(List<Object> result, Element element, String name, Class<?> type) throws Exception {
if (result == null)
return;
NodeList list = element.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node item = list.item(i);
if (item.getNodeType() == Node.ELEMENT_NODE) {
if (name.equals(((Element) item).getTagName()) || name.equals(((Element) item).getLocalName())) {
Object o = Reflect.on(type).create().get();
unmarshal0(o, (Element) item);
result.add(o);
}
}
}
}
private static Element child(Element element, String name) {
NodeList list = element.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node item = list.item(i);
if (item.getNodeType() == Node.ELEMENT_NODE)
if (name.equals(((Element) item).getTagName()) || name.equals(((Element) item).getLocalName()))
return (Element) item;
}
return null;
}
public static DocumentBuilder builder() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// -----------------------------------------------------------------
// [JOOX #136] FIX START: Prevent OWASP attack vectors
try {
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
}
catch (ParserConfigurationException ignore) {}
try {
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
}
catch (ParserConfigurationException ignore) {}
try {
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
}
catch (ParserConfigurationException ignore) {}
// [JOOX #149] Not implemented on Android
try {
factory.setXIncludeAware(false);
}
catch (UnsupportedOperationException ignore) {}
factory.setExpandEntityReferences(false);
// [JOOX #136] FIX END
// -----------------------------------------------------------------
// [JOOX #9] [JOOX #107] In order to take advantage of namespace-related DOM
// features, the internal builder should be namespace-aware
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Appends a <code>second</code> JAXB annotated object to a
* <code>first</code> one using Maven's
* <code>combine.children="append"</code> semantics.
*
* @return The modified <code>first</code> argument.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static <T> T append(T first, T second) {
if (first == null)
return second;
if (second == null)
return first;
Class<T> klass = (Class<T>) first.getClass();
if (klass != second.getClass())
throw new IllegalArgumentException("Can only append identical types");
// [#8527] support enum types
else if (klass.isEnum())
return first;
// We're assuming that XJC generated objects are all in the same package
Package pkg = klass.getPackage();
try {
T defaults = klass.getConstructor().newInstance();
for (Method setter : klass.getMethods()) {
if (setter.getName().startsWith("set")) {
Method getter;
try {
getter = klass.getMethod("get" + setter.getName().substring(3));
}
catch (NoSuchMethodException e) {
getter = klass.getMethod("is" + setter.getName().substring(3));
}
Class<?> childType = setter.getParameterTypes()[0];
Object firstChild = getter.invoke(first);
Object secondChild = getter.invoke(second);
Object defaultChild = getter.invoke(defaults);
if (Collection.class.isAssignableFrom(childType))
((List) firstChild).addAll((List) secondChild);
else if (secondChild != null && (firstChild == null || firstChild.equals(defaultChild)))
setter.invoke(first, secondChild);
else if (secondChild != null && pkg == childType.getPackage())
append(firstChild, secondChild);
else
; // All other types cannot be merged
}
}
}
catch (Exception e) {
throw new ReflectException(e);
}
return first;
}
}

View File

@ -1,116 +0,0 @@
/*
* 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.impl;
import java.util.ArrayList;
import java.util.Collection;
import org.jooq.Comparator;
import org.jooq.Condition;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.LikeEscapeStep;
import org.jooq.Operator;
final class CombinedCompareCondition extends AbstractCondition implements LikeEscapeStep {
private static final long serialVersionUID = 1850816878293293314L;
private final Field<?> field;
private final Comparator comparator;
private final Quantifier quantifier;
private final Collection<? extends Field<String>> values;
private Character escape;
public CombinedCompareCondition(Field<?> field, Comparator comparator, Quantifier quantifier, Collection<? extends Field<String>> values) {
this.field = field;
this.comparator = comparator;
this.quantifier = quantifier;
this.values = values;
}
@Override
public final Condition escape(char c) {
this.escape = c;
return this;
}
@Override
public final void accept(Context<?> ctx) {
Collection<Condition> conditions = new ArrayList<Condition>();
switch (comparator) {
case LIKE:
for (Field<String> value : values)
conditions.add(escape != null ? field.like(value, escape) : field.like(value));
break;
case NOT_LIKE:
for (Field<String> value : values)
conditions.add(escape != null ? field.notLike(value, escape) : field.notLike(value));
break;
case SIMILAR_TO:
for (Field<String> value : values)
conditions.add(escape != null ? field.similarTo(value, escape) : field.similarTo(value));
break;
case NOT_SIMILAR_TO:
for (Field<String> value : values)
conditions.add(escape != null ? field.notSimilarTo(value, escape) : field.notSimilarTo(value));
break;
case LIKE_IGNORE_CASE:
for (Field<String> value : values)
conditions.add(escape != null ? field.likeIgnoreCase(value, escape) : field.likeIgnoreCase(value));
break;
case NOT_LIKE_IGNORE_CASE:
for (Field<String> value : values)
conditions.add(escape != null ? field.notLikeIgnoreCase(value, escape) : field.notLikeIgnoreCase(value));
break;
default:
break;
}
Condition combinedCondition = CombinedCondition.of(quantifier == Quantifier.ALL ? Operator.AND : Operator.OR, conditions);
ctx.visit(combinedCondition);
}
}