diff --git a/jOOQ-meta/pom.xml b/jOOQ-meta/pom.xml
index 73257f4a81..ec2b4bd807 100644
--- a/jOOQ-meta/pom.xml
+++ b/jOOQ-meta/pom.xml
@@ -131,6 +131,54 @@
+
+
+ vertabelo-xml
+
+ generate
+
+
+ true
+ true
+ false
+ src/main/resources/xsd
+ src/main/resources/xjb/vertabelo
+
+ vertabelo-2.1.xsd
+
+ org.jooq.util.vertabelo.jaxb
+
+ -Xxew
+ -Xxew:instantiate lazy
+ -Xxew:delete
+ -Xfluent-api
+ -Xdefault-value
+ -Xannotate
+
+
+
+ com.github.jaxb-xew-plugin
+ jaxb-xew-plugin
+ 1.0
+
+
+ org.jvnet.jaxb2_commons
+ jaxb2-fluent-api
+ 3.0
+
+
+ org.jvnet.jaxb2_commons
+ jaxb2-default-value
+ 1.1
+
+
+ org.jvnet.jaxb2_commons
+ jaxb2-basics-annotate
+ 0.6.2
+
+
+
+
@@ -165,4 +213,4 @@
jooq
-
\ No newline at end of file
+
diff --git a/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLDatabase.java b/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLDatabase.java
new file mode 100644
index 0000000000..18fceb76e3
--- /dev/null
+++ b/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLDatabase.java
@@ -0,0 +1,449 @@
+package org.jooq.util.vertabelo;
+
+import java.io.File;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.xml.bind.JAXB;
+
+import org.jooq.DSLContext;
+import org.jooq.SQLDialect;
+import org.jooq.impl.DSL;
+import org.jooq.tools.JooqLogger;
+import org.jooq.tools.StringUtils;
+import org.jooq.util.AbstractDatabase;
+import org.jooq.util.ArrayDefinition;
+import org.jooq.util.CheckConstraintDefinition;
+import org.jooq.util.ColumnDefinition;
+import org.jooq.util.DataTypeDefinition;
+import org.jooq.util.DefaultCheckConstraintDefinition;
+import org.jooq.util.DefaultDataTypeDefinition;
+import org.jooq.util.DefaultRelations;
+import org.jooq.util.DefaultSequenceDefinition;
+import org.jooq.util.EnumDefinition;
+import org.jooq.util.PackageDefinition;
+import org.jooq.util.RoutineDefinition;
+import org.jooq.util.SchemaDefinition;
+import org.jooq.util.SequenceDefinition;
+import org.jooq.util.TableDefinition;
+import org.jooq.util.UDTDefinition;
+import org.jooq.util.vertabelo.jaxb.AlternateKey;
+import org.jooq.util.vertabelo.jaxb.AlternateKeyColumn;
+import org.jooq.util.vertabelo.jaxb.Column;
+import org.jooq.util.vertabelo.jaxb.DatabaseModel;
+import org.jooq.util.vertabelo.jaxb.Property;
+import org.jooq.util.vertabelo.jaxb.Reference;
+import org.jooq.util.vertabelo.jaxb.ReferenceColumn;
+import org.jooq.util.vertabelo.jaxb.Sequence;
+import org.jooq.util.vertabelo.jaxb.Table;
+import org.jooq.util.vertabelo.jaxb.TableCheck;
+import org.jooq.util.vertabelo.jaxb.View;
+
+/**
+ * The Vertabelo XML Database
+ *
+ * @author Michał Kołodziejski
+ */
+public class VertabeloXMLDatabase extends AbstractDatabase {
+
+ interface TableOperation {
+ void invoke(Table table, String schemaName);
+ }
+
+ interface ViewOperation {
+ void invoke(View view, String schemaName);
+ }
+
+
+ private static final JooqLogger log = JooqLogger.getLogger(VertabeloXMLDatabase.class);
+
+ // codegen properties
+ private static final String XML_FILE_PARAM = "xml-file";
+ private static final String DIALECT_PARAM = "dialect";
+
+ // XML additional properties
+ private static final String SCHEMA_ADDITIONAL_PROPERTY_NAME = "Schema";
+ private static final String PK_ADDITIONAL_PROPERTY_NAME = "Primary key name";
+
+
+ protected DatabaseModel databaseModel;
+
+ protected DatabaseModel databaseModel() {
+ if(databaseModel == null) {
+ databaseModel = JAXB.unmarshal(new File(getProperties().getProperty(XML_FILE_PARAM)), DatabaseModel.class);
+ }
+
+ return databaseModel;
+ }
+
+
+ @Override
+ protected DSLContext create0() {
+ @SuppressWarnings("deprecation")
+ SQLDialect dialect = SQLDialect.SQL99;
+
+ try {
+ dialect = SQLDialect.valueOf(getProperties().getProperty(DIALECT_PARAM));
+ }
+ catch (Exception ignore) {}
+
+ return DSL.using(dialect);
+ }
+
+
+ @Override
+ protected void loadPrimaryKeys(final DefaultRelations relations) throws SQLException {
+
+ filterTablesBySchema(databaseModel().getTables(), new TableOperation() {
+ @Override
+ public void invoke(Table table, String schemaName) {
+
+ SchemaDefinition schema = getSchema(schemaName);
+ TableDefinition tableDefinition = getTable(schema, table.getName());
+
+ if (tableDefinition != null) {
+ String pkName = getTablePkName(table);
+
+ // iterate through all columns and find PK columns
+ for (Column column : table.getColumns()) {
+ if (column.isPK()) {
+ relations.addPrimaryKey(pkName, tableDefinition.getColumn(column.getName()));
+ }
+ }
+ }
+ }
+ });
+ }
+
+
+ private String getTablePkName(Table table) {
+ Property pkAdditionalProperty = VertabeloXMLDatabase.findAdditionalProperty(PK_ADDITIONAL_PROPERTY_NAME,
+ table.getProperties());
+ String pkName = VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(pkAdditionalProperty);
+ if (StringUtils.isEmpty(pkName)) {
+ pkName = table.getName().toUpperCase() + "_PK";
+ }
+ return pkName;
+ }
+
+
+ @Override
+ protected void loadUniqueKeys(final DefaultRelations relations) throws SQLException {
+
+ filterTablesBySchema(databaseModel().getTables(), new TableOperation() {
+ @Override
+ public void invoke(Table table, String schemaName) {
+ SchemaDefinition schema = getSchema(schemaName);
+ TableDefinition tableDefinition = getTable(schema, table.getName());
+
+ if (tableDefinition != null) {
+ // iterate through all UNIQUE keys for this table
+ for (AlternateKey alternateKey : table.getAlternateKeys()) {
+
+ // iterate through all columns of this key
+ for (AlternateKeyColumn alternateKeyColumn : alternateKey.getColumns()) {
+ Column column = (Column) alternateKeyColumn.getColumn();
+ relations.addUniqueKey(alternateKey.getName(), tableDefinition.getColumn(column.getName()));
+ }
+
+ }
+ }
+ }
+ });
+
+ }
+
+
+ @Override
+ protected void loadForeignKeys(final DefaultRelations relations) throws SQLException {
+
+ for (final Reference reference : databaseModel().getReferences()) {
+ final Table pkTable = (Table) reference.getPKTable();
+ final Table fkTable = (Table) reference.getFKTable();
+
+ filterTablesBySchema(Arrays.asList(pkTable), new TableOperation() {
+ @Override
+ public void invoke(Table table, String schemaName) {
+ SchemaDefinition schema = getSchema(schemaName);
+ TableDefinition pkTableDefinition = getTable(schema, pkTable.getName());
+ TableDefinition fkTableDefinition = getTable(schema, fkTable.getName());
+
+ // we need to find unique key among PK and all alternate keys...
+ String uniqueKeyName = findUniqueConstraintNameForReference(reference);
+ if(uniqueKeyName == null) {
+ // no matching key - ignore this foreign key
+ return;
+ }
+
+ for (ReferenceColumn referenceColumn : reference.getReferenceColumns()) {
+ Column fkColumn = (Column) referenceColumn.getFKColumn();
+ ColumnDefinition fkColumnDefinition = fkTableDefinition.getColumn(fkColumn.getName());
+
+ relations.addForeignKey(
+ reference.getName(),
+ uniqueKeyName,
+ fkColumnDefinition,
+ pkTableDefinition.getSchema());
+ }
+ }
+ });
+
+ }
+ }
+
+
+ private String findUniqueConstraintNameForReference(Reference reference) {
+ // list of referenced columns
+ List uniqueKeyColumns = new ArrayList();
+ for (ReferenceColumn referenceColumn : reference.getReferenceColumns()) {
+ uniqueKeyColumns.add((Column) referenceColumn.getPKColumn());
+ }
+
+
+ // list of PK columns
+ Table pkTable = (Table) reference.getPKTable();
+ List pkColumns = new ArrayList();
+ for (Column column : pkTable.getColumns()) {
+ if (column.isPK()) {
+ pkColumns.add(column);
+ }
+ }
+
+ if (uniqueKeyColumns.equals(pkColumns)) {
+ // PK matches FK
+ log.info("Primary key constraint matches foreign key: " + reference.getName());
+ return getTablePkName((Table) reference.getPKTable());
+ }
+
+ // need to check alternate keys
+ for (AlternateKey alternateKey : pkTable.getAlternateKeys()) {
+ List akColumns = new ArrayList();
+ for (AlternateKeyColumn column : alternateKey.getColumns()) {
+ akColumns.add((Column) column.getColumn());
+ }
+
+ if (uniqueKeyColumns.equals(akColumns)) {
+ // AK matches FK
+ log.info("Alternate key constraint matches foreign key: " + reference.getName());
+ return alternateKey.getName();
+ }
+ }
+
+ // no match
+ log.info("No matching unique constraint for foreign key: " + reference.getName());
+ return null;
+ }
+
+
+
+ @Override
+ protected void loadCheckConstraints(final DefaultRelations relations) throws SQLException {
+
+ filterTablesBySchema(databaseModel().getTables(), new TableOperation() {
+ @Override
+ public void invoke(Table table, String schemaName) {
+ SchemaDefinition schema = getSchema(schemaName);
+ TableDefinition tableDefinition = getTable(schema, table.getName());
+
+ if (tableDefinition != null) {
+
+ // iterate through all table checks
+ for (TableCheck tableCheck : table.getTableChecks()) {
+ CheckConstraintDefinition checkConstraintDefinition = new DefaultCheckConstraintDefinition(
+ schema,
+ tableDefinition,
+ tableCheck.getName(),
+ tableCheck.getCheckExpression());
+
+ relations.addCheckConstraint(tableDefinition, checkConstraintDefinition);
+ }
+
+ // iterate through all columns and find columns with checks
+ for (Column column : table.getColumns()) {
+ if (! StringUtils.isBlank(column.getCheckExpression())) {
+ CheckConstraintDefinition checkConstraintDefinition = new DefaultCheckConstraintDefinition(
+ schema,
+ tableDefinition,
+ table.getName() + "_" + column.getName() + "_check",
+ column.getCheckExpression());
+
+ relations.addCheckConstraint(tableDefinition, checkConstraintDefinition);
+ }
+ }
+ }
+ }
+ });
+ }
+
+
+ @Override
+ protected List getSchemata0() throws SQLException {
+ List result = new ArrayList();
+ List schemaNames = new ArrayList();
+
+ // search in tables
+ for (Table table : databaseModel().getTables()) {
+ Property additionalProperty = findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME, table.getProperties());
+ addUniqueSchemaName(additionalProperty, schemaNames);
+ }
+
+ // search in views
+ for (View view : databaseModel().getViews()) {
+ Property additionalProperty = findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME, view.getProperties());
+ addUniqueSchemaName(additionalProperty, schemaNames);
+ }
+
+
+ // transform
+ for (String schemaName : schemaNames) {
+ result.add(new SchemaDefinition(this, schemaName, null));
+ }
+
+ return result;
+ }
+
+
+ private void addUniqueSchemaName(Property additionalProperty, List schemaNames) {
+ String schemaName = ""; // default to empty string
+ if (additionalProperty != null) {
+ // additional property is set
+ schemaName = additionalProperty.getValue();
+ }
+
+ if (!schemaNames.contains(schemaName)) {
+ schemaNames.add(schemaName);
+ }
+ }
+
+
+ @Override
+ protected List getSequences0() throws SQLException {
+ List result = new ArrayList();
+
+ for (Sequence sequence : databaseModel().getSequences()) {
+ Property additionalProperty = VertabeloXMLDatabase.findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME,
+ sequence.getProperties());
+ String schemaName = VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(additionalProperty);
+
+ if (getInputSchemata().contains(schemaName)) {
+ SchemaDefinition schema = getSchema(schemaName);
+
+ DataTypeDefinition type = new DefaultDataTypeDefinition(
+ this,
+ schema,
+ "BIGINT"
+ );
+
+ result.add(new DefaultSequenceDefinition(schema, sequence.getName(), type));
+ }
+ }
+
+ return result;
+ }
+
+
+ @Override
+ protected List getTables0() throws SQLException {
+ final List result = new ArrayList();
+
+ // tables
+ filterTablesBySchema(databaseModel().getTables(), new TableOperation() {
+ @Override
+ public void invoke(Table table, String schemaName) {
+ SchemaDefinition schema = getSchema(schemaName);
+ result.add(new VertabeloXMLTableDefinition(schema, table));
+ }
+ });
+
+ // views
+ filterViewsBySchema(databaseModel().getViews(), new ViewOperation() {
+ @Override
+ public void invoke(View view, String schemaName) {
+ SchemaDefinition schema = getSchema(schemaName);
+ result.add(new VertabeloXMLTableDefinition(schema, view));
+ }
+ });
+
+ return result;
+ }
+
+
+ @Override
+ protected List getEnums0() {
+ List result = new ArrayList();
+ return result;
+ }
+
+ @Override
+ protected List getUDTs0() {
+ List result = new ArrayList();
+ return result;
+ }
+
+ @Override
+ protected List getArrays0() {
+ List result = new ArrayList();
+ return result;
+ }
+
+ @Override
+ protected List getRoutines0() {
+ List result = new ArrayList();
+ return result;
+ }
+
+ @Override
+ protected List getPackages0() {
+ List result = new ArrayList();
+ return result;
+ }
+
+
+ protected void filterTablesBySchema(List tables, TableOperation operation) {
+ for (Table table : tables) {
+ Property schemaAdditionalProperty = VertabeloXMLDatabase.findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME,
+ table.getProperties());
+ String schemaName = VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(schemaAdditionalProperty);
+
+ if (getInputSchemata().contains(schemaName)) {
+
+ operation.invoke(table, schemaName);
+
+ }
+ }
+ }
+
+
+ protected void filterViewsBySchema(List views, ViewOperation operation) {
+ for (View view : views) {
+ Property schemaAdditionalProperty = VertabeloXMLDatabase.findAdditionalProperty(SCHEMA_ADDITIONAL_PROPERTY_NAME,
+ view.getProperties());
+ String schemaName = VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(schemaAdditionalProperty);
+
+ if (getInputSchemata().contains(schemaName)) {
+
+ operation.invoke(view, schemaName);
+
+ }
+ }
+ }
+
+
+ public static Property findAdditionalProperty(String name, List properties) {
+ for (Property property : properties) {
+ if (property.getName().equalsIgnoreCase(name)) {
+ return property;
+ }
+ }
+ return null;
+ }
+
+ public static String getAdditionalPropertyValueOrEmpty(Property additionalProperty) {
+ if (additionalProperty != null) {
+ return additionalProperty.getValue();
+ }
+ return "";
+ }
+}
diff --git a/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLTableDefinition.java b/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLTableDefinition.java
new file mode 100644
index 0000000000..88677b3185
--- /dev/null
+++ b/jOOQ-meta/src/main/java/org/jooq/util/vertabelo/VertabeloXMLTableDefinition.java
@@ -0,0 +1,144 @@
+package org.jooq.util.vertabelo;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jooq.DataType;
+import org.jooq.impl.DefaultDataType;
+import org.jooq.tools.StringUtils;
+import org.jooq.util.AbstractTableDefinition;
+import org.jooq.util.ColumnDefinition;
+import org.jooq.util.DataTypeDefinition;
+import org.jooq.util.DefaultColumnDefinition;
+import org.jooq.util.DefaultDataTypeDefinition;
+import org.jooq.util.SchemaDefinition;
+import org.jooq.util.vertabelo.jaxb.Column;
+import org.jooq.util.vertabelo.jaxb.Property;
+import org.jooq.util.vertabelo.jaxb.Table;
+import org.jooq.util.vertabelo.jaxb.View;
+import org.jooq.util.vertabelo.jaxb.ViewColumn;
+
+/**
+ * Definition of the Vertabelo XML Table
+ *
+ * @author Michał Kołodziejski
+ */
+public class VertabeloXMLTableDefinition extends AbstractTableDefinition {
+
+ protected Table table;
+ protected View view;
+
+ public VertabeloXMLTableDefinition(SchemaDefinition schema, Table table) {
+ super(schema, table.getName(), "");
+
+ this.table = table;
+ }
+
+ public VertabeloXMLTableDefinition(SchemaDefinition schema, View view) {
+ super(schema, view.getName(), "");
+
+ this.view = view;
+ }
+
+
+ @Override
+ protected List getElements0() throws SQLException {
+ if(table != null) {
+ // table
+ return getTableElements();
+
+ } else {
+ // view
+ return getViewElements();
+ }
+ }
+
+
+ protected List getTableElements() {
+ List result = new ArrayList();
+
+ String schemaName = getSchemaName();
+ SchemaDefinition schema = getDatabase().getSchema(schemaName);
+
+ int position = 0;
+ for(Column column : table.getColumns()) {
+ ++position;
+
+ // convert data type
+ DataType> dataType = DefaultDataType.getDataType(getDialect(), column.getType());
+
+ DataTypeDefinition type = new DefaultDataTypeDefinition(
+ getDatabase(),
+ schema,
+ dataType.getTypeName(),
+ dataType.hasLength() ? dataType.length() : null,
+ dataType.hasPrecision() ? dataType.precision() : null,
+ dataType.hasScale() ? dataType.scale() : null,
+ column.isNullable(),
+ !StringUtils.isEmpty(column.getDefaultValue()));
+
+ ColumnDefinition columnDefinition = new DefaultColumnDefinition(
+ this,
+ column.getName(),
+ position,
+ type,
+ false,
+ column.getDescription());
+
+ result.add(columnDefinition);
+ }
+
+ return result;
+ }
+
+
+ protected List getViewElements() {
+ List result = new ArrayList();
+
+ String schemaName = getSchemaName();
+ SchemaDefinition schema = getDatabase().getSchema(schemaName);
+
+ int position = 0;
+ for(ViewColumn column : view.getViewColumns()) {
+ ++position;
+
+ // convert data type
+ DataType> dataType = DefaultDataType.getDataType(getDialect(), column.getType());
+
+ DataTypeDefinition type = new DefaultDataTypeDefinition(
+ getDatabase(),
+ schema,
+ dataType.getTypeName(),
+ dataType.hasLength() ? dataType.length() : null,
+ dataType.hasPrecision() ? dataType.precision() : null,
+ dataType.hasScale() ? dataType.scale() : null,
+ true,
+ false);
+
+ ColumnDefinition columnDefinition = new DefaultColumnDefinition(
+ this,
+ column.getName(),
+ position,
+ type,
+ false,
+ column.getDescription());
+
+ result.add(columnDefinition);
+ }
+
+ return result;
+ }
+
+ protected String getSchemaName() {
+ Property additionalProperty;
+
+ if(table != null) {
+ additionalProperty = VertabeloXMLDatabase.findAdditionalProperty("Schema", table.getProperties());
+ } else {
+ additionalProperty = VertabeloXMLDatabase.findAdditionalProperty("Schema", view.getProperties());
+ }
+
+ return VertabeloXMLDatabase.getAdditionalPropertyValueOrEmpty(additionalProperty);
+ }
+}
diff --git a/jOOQ-meta/src/main/resources/xjb/vertabelo/binding.xjb b/jOOQ-meta/src/main/resources/xjb/vertabelo/binding.xjb
new file mode 100644
index 0000000000..03fefad849
--- /dev/null
+++ b/jOOQ-meta/src/main/resources/xjb/vertabelo/binding.xjb
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jOOQ-meta/src/main/resources/xsd/vertabelo-2.1.xsd b/jOOQ-meta/src/main/resources/xsd/vertabelo-2.1.xsd
new file mode 100644
index 0000000000..e38cf7de9d
--- /dev/null
+++ b/jOOQ-meta/src/main/resources/xsd/vertabelo-2.1.xsd
@@ -0,0 +1,439 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+