All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jooq.impl.InformationSchemaMetaImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com)
 * All rights reserved.
 *
 * 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 static java.util.Collections.unmodifiableList;
import static org.jooq.impl.DSL.name;
import static org.jooq.util.xml.jaxb.TableConstraintType.PRIMARY_KEY;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXB;

import org.jooq.Catalog;
import org.jooq.Configuration;
import org.jooq.DataType;
import org.jooq.ForeignKey;
import org.jooq.Meta;
import org.jooq.Name;
import org.jooq.Record;
import org.jooq.Schema;
import org.jooq.Sequence;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.UniqueKey;
import org.jooq.exception.SQLDialectNotSupportedException;
import org.jooq.util.xml.jaxb.Column;
import org.jooq.util.xml.jaxb.InformationSchema;
import org.jooq.util.xml.jaxb.KeyColumnUsage;
import org.jooq.util.xml.jaxb.ReferentialConstraint;
import org.jooq.util.xml.jaxb.TableConstraint;

/**
 * @author Lukas Eder
 */
final class InformationSchemaMetaImpl implements Meta {

    private final Configuration                             configuration;
    private final InformationSchema                         source;

    private final List                             catalogs;
    private final List                              schemas;
    private final Map                         schemasByName;
    private final Map>                schemasPerCatalog;
    private final List              tables;
    private final Map         tablesByName;
    private final Map> tablesPerSchema;
    private final List>                         sequences;
    private final Map>>            sequencesPerSchema;
    private final List>               primaryKeys;
    private final Map>          uniqueKeysByName;
    private final Map                           referentialKeys;

    InformationSchemaMetaImpl(Configuration configuration, InformationSchema source) {
        this.configuration = configuration;
        this.source = source;
        this.catalogs = new ArrayList();
        this.schemas = new ArrayList();
        this.schemasByName = new HashMap();
        this.schemasPerCatalog = new HashMap>();
        this.tables = new ArrayList();
        this.tablesByName = new HashMap();
        this.tablesPerSchema = new HashMap>();
        this.sequences = new ArrayList>();
        this.sequencesPerSchema = new HashMap>>();
        this.primaryKeys = new ArrayList>();
        this.uniqueKeysByName = new HashMap>();
        this.referentialKeys = new HashMap();

        init(source);
    }
public static void main(String[] args) {
    System.out.println(String.format("x y %0$s", "abc"));
}
    private final void init(InformationSchema meta) {

        List errors = new ArrayList();

        // Catalogs / Schemas
        // -------------------------------------------------------------------------------------------------------------
        for (org.jooq.util.xml.jaxb.Schema xs : meta.getSchemata()) {
            InformationSchemaCatalog catalog = new InformationSchemaCatalog(xs.getCatalogName());

            if (!catalogs.contains(catalog))
                catalogs.add(catalog);

            InformationSchemaSchema is = new InformationSchemaSchema(xs.getSchemaName(), catalog);
            schemas.add(is);
            schemasByName.put(name(xs.getCatalogName(), xs.getSchemaName()), is);
        }

        // Tables
        // -------------------------------------------------------------------------------------------------------------
        tableLoop:
        for (org.jooq.util.xml.jaxb.Table xt : meta.getTables()) {
            Name schemaName = name(xt.getTableCatalog(), xt.getTableSchema());
            Schema schema = schemasByName.get(schemaName);

            if (schema == null) {
                errors.add(String.format("Schema " + schemaName + " not defined for table " + xt.getTableName()));
                continue tableLoop;
            }

            InformationSchemaTable it = new InformationSchemaTable(xt.getTableName(), schema);
            tables.add(it);
            tablesByName.put(name(xt.getTableCatalog(), xt.getTableSchema(), xt.getTableName()), it);
        }

        // Columns
        // -------------------------------------------------------------------------------------------------------------
        List columns = new ArrayList(meta.getColumns());
        Collections.sort(columns, new Comparator() {
            @Override
            public int compare(Column o1, Column o2) {
                Integer p1 = o1.getOrdinalPosition();
                Integer p2 = o2.getOrdinalPosition();

                if (p1 == p2)
                    return 0;
                if (p1 == null)
                    return -1;
                if (p2 == null)
                    return 1;

                return p1.compareTo(p2);
            }
        });

        columnLoop:
        for (Column xc : columns) {
            String typeName = xc.getDataType();
            int length = xc.getCharacterMaximumLength() == null ? 0 : xc.getCharacterMaximumLength();
            int precision = xc.getNumericPrecision() == null ? 0 : xc.getNumericPrecision();
            int scale = xc.getNumericScale() == null ? 0 : xc.getNumericScale();
            boolean nullable = xc.isIsNullable() == null ? true : xc.isIsNullable();

            // TODO: Exception handling should be moved inside SQLDataType
            Name tableName = name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName());
            InformationSchemaTable table = tablesByName.get(tableName);

            if (table == null) {
                errors.add(String.format("Table " + tableName + " not defined for column " + xc.getColumnName()));
                continue columnLoop;
            }

            AbstractTable.createField(
                xc.getColumnName(),
                type(typeName, length, precision, scale, nullable),
                table
            );
        }

        // Constraints
        // -------------------------------------------------------------------------------------------------------------
        Map>> columnsByConstraint = new HashMap>>();
        List keyColumnUsages = new ArrayList(meta.getKeyColumnUsages());
        Collections.sort(keyColumnUsages, new Comparator() {
            @Override
            public int compare(KeyColumnUsage o1, KeyColumnUsage o2) {
                int p1 = o1.getOrdinalPosition();
                int p2 = o2.getOrdinalPosition();

                return (p1 < p2) ? -1 : ((p1 == p2) ? 0 : 1);
            }
        });

        keyColumnLoop:
        for (KeyColumnUsage xc : keyColumnUsages) {
            Name constraintName = name(xc.getConstraintCatalog(), xc.getConstraintSchema(), xc.getConstraintName());
            List> fields = columnsByConstraint.get(constraintName);

            if (fields == null) {
                fields = new ArrayList>();
                columnsByConstraint.put(constraintName, fields);
            }

            Name tableName = name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName());
            InformationSchemaTable table = tablesByName.get(tableName);

            if (table == null) {
                errors.add(String.format("Table " + tableName + " not defined for constraint " + constraintName));
                continue keyColumnLoop;
            }

            TableField field = (TableField) table.field(xc.getColumnName());

            if (field == null) {
                errors.add(String.format("Column " + xc.getColumnName() + " not defined for table " + tableName));
                continue keyColumnLoop;
            }

            fields.add(field);
        }

        tableConstraintLoop:
        for (TableConstraint xc : meta.getTableConstraints()) {
            switch (xc.getConstraintType()) {
                case PRIMARY_KEY:
                case UNIQUE: {
                    Name tableName = name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName());
                    Name constraintName = name(xc.getConstraintCatalog(), xc.getConstraintSchema(), xc.getConstraintName());
                    InformationSchemaTable table = tablesByName.get(tableName);

                    if (table == null) {
                        errors.add(String.format("Table " + tableName + " not defined for constraint " + constraintName));
                        continue tableConstraintLoop;
                    }

                    List> c = columnsByConstraint.get(constraintName);

                    if (c == null || c.isEmpty()) {
                        errors.add(String.format("No columns defined for constraint " + constraintName));
                        continue tableConstraintLoop;
                    }

                    UniqueKeyImpl key = (UniqueKeyImpl) AbstractKeys.createUniqueKey(table, xc.getConstraintName(), c.toArray(new TableField[0]));

                    if (xc.getConstraintType() == PRIMARY_KEY) {
                        table.primaryKey = key;
                        primaryKeys.add(key);
                    }

                    table.uniqueKeys.add(key);
                    uniqueKeysByName.put(constraintName, key);
                    break;
                }
            }
        }

        for (ReferentialConstraint xr : meta.getReferentialConstraints()) {
            referentialKeys.put(
                name(xr.getConstraintCatalog(), xr.getConstraintSchema(), xr.getConstraintName()),
                name(xr.getUniqueConstraintCatalog(), xr.getUniqueConstraintSchema(), xr.getUniqueConstraintName())
            );
        }

        tableConstraintLoop:
        for (TableConstraint xc : meta.getTableConstraints()) {
            switch (xc.getConstraintType()) {
                case FOREIGN_KEY: {
                    Name tableName = name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName());
                    Name constraintName = name(xc.getConstraintCatalog(), xc.getConstraintSchema(), xc.getConstraintName());
                    InformationSchemaTable table = tablesByName.get(tableName);

                    if (table == null) {
                        errors.add(String.format("Table " + tableName + " not defined for constraint " + constraintName));
                        continue tableConstraintLoop;
                    }

                    List> c = columnsByConstraint.get(constraintName);

                    if (c == null || c.isEmpty()) {
                        errors.add(String.format("No columns defined for constraint " + constraintName));
                        continue tableConstraintLoop;
                    }

                    UniqueKeyImpl uniqueKey = uniqueKeysByName.get(referentialKeys.get(constraintName));

                    if (uniqueKey == null) {
                        errors.add(String.format("No unique key defined for foreign key " + constraintName));
                        continue tableConstraintLoop;
                    }

                    ForeignKey key = AbstractKeys.createForeignKey(uniqueKey, table, xc.getConstraintName(), c.toArray(new TableField[0]));
                    table.foreignKeys.add(key);
                    break;
                }
            }
        }

        // Sequences
        // -------------------------------------------------------------------------------------------------------------
        sequenceLoop:
        for (org.jooq.util.xml.jaxb.Sequence xs : meta.getSequences()) {
            Name schemaName = name(xs.getSequenceCatalog(), xs.getSequenceSchema());
            Schema schema = schemasByName.get(schemaName);

            if (schema == null) {
                errors.add(String.format("Schema " + schemaName + " not defined for sequence " + xs.getSequenceName()));
                continue sequenceLoop;
            }

            String typeName = xs.getDataType();
            int length = xs.getCharacterMaximumLength() == null ? 0 : xs.getCharacterMaximumLength();
            int precision = xs.getNumericPrecision() == null ? 0 : xs.getNumericPrecision();
            int scale = xs.getNumericScale() == null ? 0 : xs.getNumericScale();
            boolean nullable = true;

            @SuppressWarnings({ "rawtypes", "unchecked" })
            InformationSchemaSequence is = new InformationSchemaSequence(
                xs.getSequenceName(),
                schema,
                type(typeName, length, precision, scale, nullable)
            );

            sequences.add(is);
        }

        // Lookups
        // -------------------------------------------------------------------------------------------------------------
        for (Schema s : schemas) {
            Catalog c = s.getCatalog();
            List list = schemasPerCatalog.get(c);

            if (list == null) {
                list = new ArrayList();
                schemasPerCatalog.put(c, list);
            }

            list.add(s);
        }

        for (InformationSchemaTable t : tables) {
            Schema s = t.getSchema();
            List list = tablesPerSchema.get(s);

            if (list == null) {
                list = new ArrayList();
                tablesPerSchema.put(s, list);
            }

            list.add(t);
        }

        for (Sequence q : sequences) {
            Schema s = q.getSchema();
            List> list = sequencesPerSchema.get(s);

            if (list == null) {
                list = new ArrayList>();
                sequencesPerSchema.put(s, list);
            }

            list.add(q);
        }

        if (!errors.isEmpty())
            throw new IllegalArgumentException(errors.toString());
    }

    private final DataType type(String typeName, int length, int precision, int scale, boolean nullable) {
        DataType type = null;

        try {
            type = DefaultDataType.getDataType(configuration.family(), typeName);
            type = type.nullable(nullable);

            if (length != 0)
                type = type.length(length);
            else if (precision != 0 || scale != 0)
                type = type.precision(precision, scale);
        }
        catch (SQLDialectNotSupportedException e) {
            type = SQLDataType.OTHER;
        }

        return type;
    }

    @Override
    public final List getCatalogs() {
        return Collections.unmodifiableList(catalogs);
    }

    @Override
    public final List getSchemas() {
        return Collections.unmodifiableList(schemas);
    }

    @Override
    public final List> getTables() {
        return Collections.>unmodifiableList(tables);
    }

    @Override
    public final List> getSequences() {
        return Collections.>unmodifiableList(sequences);
    }

    @Override
    public final List> getPrimaryKeys() {
        return Collections.>unmodifiableList(primaryKeys);
    }

    private final class InformationSchemaCatalog extends CatalogImpl {

        /**
         * Generated UID
         */
        private static final long serialVersionUID = 87038321849045492L;

        InformationSchemaCatalog(String name) {
            super(name);
        }

        @Override
        public final List getSchemas() {
            return unmodifiableList(schemasPerCatalog.get(this));
        }
    }

    private final class InformationSchemaSchema extends SchemaImpl {

        /**
         * Generated UID
         */
        private static final long serialVersionUID = 7290709749127378187L;

        public InformationSchemaSchema(String name, Catalog catalog) {
            super(name, catalog);
        }

        @Override
        public final List> getTables() {
            return Collections.>unmodifiableList(tablesPerSchema.get(this));
        }

        @Override
        public final List> getSequences() {
            return Collections.>unmodifiableList(sequencesPerSchema.get(this));
        }
    }

    private final class InformationSchemaTable extends TableImpl {

        /**
         * Generated UID
         */
        private static final long              serialVersionUID = 4314110578549768267L;

        UniqueKey                      primaryKey;
        final List>          uniqueKeys       = new ArrayList>();
        final List> foreignKeys      = new ArrayList>();

        public InformationSchemaTable(String name, Schema schema) {
            super(name, schema);
        }

        @Override
        public UniqueKey getPrimaryKey() {
            return primaryKey;
        }

        @Override
        public List> getKeys() {
            return Collections.>unmodifiableList(uniqueKeys);
        }

        @Override
        public List> getReferences() {
            return Collections.>unmodifiableList(foreignKeys);
        }
    }

    private final class InformationSchemaSequence extends SequenceImpl {

        /**
         * Generated UID
         */
        private static final long serialVersionUID = -1246697252597049756L;

        InformationSchemaSequence(String name, Schema schema, DataType type) {
            super(name, schema, type);
        }
    }

    @Override
    public String toString() {
        StringWriter writer = new StringWriter();
        JAXB.marshal(source, writer);
        return writer.toString();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy