org.everit.osgi.dev.lqmg.internal.LQMGMetadataExporter.java Source code

Java tutorial

Introduction

Here is the source code for org.everit.osgi.dev.lqmg.internal.LQMGMetadataExporter.java

Source

/**
 * This file is part of Everit - Liquibase-QueryDSL Model Generator.
 *
 * Everit - Liquibase-QueryDSL Model Generator is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Everit - Liquibase-QueryDSL Model Generator is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Everit - Liquibase-QueryDSL Model Generator.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.everit.osgi.dev.lqmg.internal;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.everit.osgi.dev.lqmg.internal.schema.xml.AbstractNamingRuleType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.io.Files;
import com.mysema.codegen.CodeWriter;
import com.mysema.codegen.JavaWriter;
import com.mysema.codegen.model.ClassType;
import com.mysema.codegen.model.SimpleType;
import com.mysema.codegen.model.Type;
import com.mysema.codegen.model.TypeCategory;
import com.mysema.query.codegen.EntityType;
import com.mysema.query.codegen.JavaTypeMappings;
import com.mysema.query.codegen.Property;
import com.mysema.query.codegen.Serializer;
import com.mysema.query.codegen.SimpleSerializerConfig;
import com.mysema.query.codegen.TypeMappings;
import com.mysema.query.sql.ColumnImpl;
import com.mysema.query.sql.ColumnMetadata;
import com.mysema.query.sql.Configuration;
import com.mysema.query.sql.codegen.MetaDataSerializer;
import com.mysema.query.sql.codegen.NamingStrategy;
import com.mysema.query.sql.support.ForeignKeyData;
import com.mysema.query.sql.support.InverseForeignKeyData;
import com.mysema.query.sql.support.NotNullImpl;
import com.mysema.query.sql.support.PrimaryKeyData;
import com.mysema.query.sql.support.SizeImpl;

public class LQMGMetadataExporter {

    private static Logger LOGGER = LoggerFactory.getLogger(LQMGMetadataExporter.class);

    private final ConfigurationContainer configurationContainer;

    private final Set<String> packages;

    private TypeMappings typeMappings;

    private Serializer serializer;

    private boolean columnAnnotations = false;

    private Configuration configuration;

    private boolean validationAnnotations = false;

    private final Set<String> classes = new HashSet<String>();

    private File targetFolder;

    private String sourceEncoding = "UTF-8";

    protected NamingStrategy namingStrategy;

    private final LQMGKeyDataFactory keyDataFactory;

    private boolean innerClassesForKeys;

    public LQMGMetadataExporter(final ConfigurationContainer configurationContainer, final String[] packages) {
        this.configurationContainer = configurationContainer;
        namingStrategy = new LQMGNamingStrategy(configurationContainer);
        this.packages = new HashSet<String>(Arrays.asList(packages));
        keyDataFactory = new LQMGKeyDataFactory(configurationContainer);
    }

    protected EntityType createEntityType(final String schemaName, final String tableName, final String className) {

        ConfigValue<? extends AbstractNamingRuleType> configValue = configurationContainer
                .findConfigForEntity(schemaName, tableName);
        AbstractNamingRuleType namingRule = configValue.getNamingRule();

        EntityType classModel;
        String packageName = namingRule.getPackage();
        Type classTypeModel = new SimpleType(TypeCategory.ENTITY, packageName + "." + className, packageName,
                className, false, false);
        classModel = new EntityType(classTypeModel);
        typeMappings.register(classModel, classModel);

        classModel.getData().put("schema", schemaName);
        classModel.getData().put("table", tableName);
        return classModel;
    }

    protected Property createProperty(final EntityType classModel, final String normalizedColumnName,
            final String propertyName, final Type typeModel) {
        return new Property(classModel, propertyName, propertyName, typeModel, Collections.<String>emptyList(),
                false);
    }

    public void setInnerClassesForKeys(boolean innerClassesForKeys) {
        this.innerClassesForKeys = innerClassesForKeys;
    }

    /**
     * Export the tables based on the given database metadata
     *
     * @param md
     * @throws SQLException
     */
    public void export(final DatabaseMetaData md) throws SQLException {
        typeMappings = new JavaTypeMappings();
        serializer = new MetaDataSerializer(typeMappings, namingStrategy, innerClassesForKeys,
                Collections.<String>emptySet());
        configuration = Configuration.DEFAULT;

        List<String> types = new ArrayList<String>(2);
        types.add("TABLE");
        types.add("VIEW");

        ResultSet tables = md.getTables(null, null, null, types.toArray(new String[types.size()]));
        try {
            while (tables.next()) {
                handleTable(md, tables);
            }
        } finally {
            tables.close();
        }

    }

    private void handleColumn(final EntityType classModel, final String tableName, final ResultSet columns)
            throws SQLException {
        String columnName = columns.getString("COLUMN_NAME");
        String normalizedColumnName = namingStrategy.normalizeColumnName(columnName);
        int columnType = columns.getInt("DATA_TYPE");
        Number columnSize = (Number) columns.getObject("COLUMN_SIZE");
        Number columnDigits = (Number) columns.getObject("DECIMAL_DIGITS");
        int nullable = columns.getInt("NULLABLE");

        String propertyName = namingStrategy.getPropertyName(normalizedColumnName, classModel);
        Class<?> clazz = configuration.getJavaType(columnType, columnSize != null ? columnSize.intValue() : 0,
                columnDigits != null ? columnDigits.intValue() : 0, tableName, columnName);
        if (clazz == null) {
            throw new IllegalStateException(
                    "Found no mapping for " + columnType + " (" + tableName + "." + columnName + ")");
        }
        TypeCategory fieldType = TypeCategory.get(clazz.getName());
        if (Number.class.isAssignableFrom(clazz)) {
            fieldType = TypeCategory.NUMERIC;
        } else if (Enum.class.isAssignableFrom(clazz)) {
            fieldType = TypeCategory.ENUM;
        }
        Type typeModel = new ClassType(fieldType, clazz);
        Property property = createProperty(classModel, normalizedColumnName, propertyName, typeModel);
        ColumnMetadata column = ColumnMetadata.named(normalizedColumnName).ofType(columnType);
        if (nullable == DatabaseMetaData.columnNoNulls) {
            column = column.notNull();
        }
        if (columnSize != null) {
            column = column.withSize(columnSize.intValue());
        }
        if (columnDigits != null) {
            column = column.withDigits(columnDigits.intValue());
        }
        property.getData().put("COLUMN", column);

        if (columnAnnotations) {
            property.addAnnotation(new ColumnImpl(normalizedColumnName));
        }
        if (validationAnnotations) {
            if (nullable == DatabaseMetaData.columnNoNulls) {
                property.addAnnotation(new NotNullImpl());
            }
            int size = columns.getInt("COLUMN_SIZE");
            if ((size > 0) && clazz.equals(String.class)) {
                property.addAnnotation(new SizeImpl(0, size));
            }
        }
        classModel.addProperty(property);
    }

    protected void handleTable(final DatabaseMetaData md, final ResultSet tables) throws SQLException {
        String schema = tables.getString("TABLE_SCHEM");
        String entity = tables.getString("TABLE_NAME");
        ConfigValue<? extends AbstractNamingRuleType> configValue = configurationContainer
                .findConfigForEntity(schema, entity);
        if (configValue == null) {
            LOGGER.info("No configuration for table '" + entity + "' in schema '" + schema
                    + "'. Ignoring from metadata class generation");
            return;
        }
        AbstractNamingRuleType namingRule = configValue.getNamingRule();

        String javaPackage = namingRule.getPackage();
        if ((packages.size() > 0) && !packages.contains(javaPackage)) {
            LOGGER.info("Java package '" + javaPackage + "' is not included, ignoring entity '" + entity
                    + "' from schema '" + schema + "'.");
            return;
        }

        String catalog = tables.getString("TABLE_CAT");
        String tableName = tables.getString("TABLE_NAME");
        String normalizedTableName = namingStrategy.normalizeTableName(tableName);
        String className = configurationContainer.resolveClassName(schema, tableName, namingStrategy);
        EntityType classModel = createEntityType(schema, normalizedTableName, className);

        // collect primary keys
        Map<String, PrimaryKeyData> primaryKeyData = keyDataFactory.getPrimaryKeys(md, catalog, schema, tableName);
        if (!primaryKeyData.isEmpty()) {
            classModel.getData().put(PrimaryKeyData.class, primaryKeyData.values());
        }

        // collect foreign keys
        Map<String, ForeignKeyData> foreignKeyData = keyDataFactory.getImportedKeys(md, catalog, schema, tableName);
        if (!foreignKeyData.isEmpty()) {
            classModel.getData().put(ForeignKeyData.class, foreignKeyData.values());
        }

        // collect inverse foreign keys
        Map<String, InverseForeignKeyData> inverseForeignKeyData = keyDataFactory.getExportedKeys(md, catalog,
                schema, tableName);
        if (!inverseForeignKeyData.isEmpty()) {
            classModel.getData().put(InverseForeignKeyData.class, inverseForeignKeyData.values());
        }

        // collect columns
        ResultSet columns = md.getColumns(catalog, schema, tableName.replace("/", "//"), null);
        try {
            while (columns.next()) {
                handleColumn(classModel, tableName, columns);
            }
        } finally {
            columns.close();
        }

        // serialize model
        serialize(classModel);

        LOGGER.info("Exported " + tableName + " successfully");
    }

    private void serialize(final EntityType type) {
        try {
            String fileSuffix = ".java";

            String packageName = type.getPackageName();
            String path = packageName.replace('.', '/') + "/" + type.getSimpleName() + fileSuffix;
            write(serializer, path, type);

        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * @param columnAnnotations
     */
    public void setColumnAnnotations(final boolean columnAnnotations) {
        this.columnAnnotations = columnAnnotations;
    }

    /**
     * Set the target folder
     *
     * @param targetFolder
     *            target source folder to create the sources into (e.g. target/generated-sources/java)
     */
    public void setTargetFolder(final File targetFolder) {
        this.targetFolder = targetFolder;
    }

    /**
     * @param validationAnnotations
     */
    public void setValidationAnnotations(final boolean validationAnnotations) {
        this.validationAnnotations = validationAnnotations;
    }

    private void write(final Serializer serializer, final String path, final EntityType type) throws IOException {
        File targetFile = new File(targetFolder, path);
        classes.add(targetFile.getPath());
        StringWriter w = new StringWriter();
        CodeWriter writer = new JavaWriter(w);
        serializer.serialize(type, SimpleSerializerConfig.DEFAULT, writer);

        // conditional creation
        boolean generate = true;
        byte[] bytes = w.toString().getBytes(sourceEncoding);
        if (targetFile.exists() && (targetFile.length() == bytes.length)) {
            String str = Files.toString(targetFile, Charset.forName(sourceEncoding));
            if (str.equals(w.toString())) {
                generate = false;
            }
        } else {
            targetFile.getParentFile().mkdirs();
        }

        if (generate) {
            Files.write(bytes, targetFile);
        }
    }
}