org.apache.torque.templates.transformer.sql.SQLTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.torque.templates.transformer.sql.SQLTransformer.java

Source

package org.apache.torque.templates.transformer.sql;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.torque.generator.control.ControllerState;
import org.apache.torque.generator.source.SourceElement;
import org.apache.torque.generator.source.SourcePath;
import org.apache.torque.generator.source.transform.SourceTransformer;
import org.apache.torque.generator.source.transform.SourceTransformerException;
import org.apache.torque.templates.TemplateOptionName;
import org.apache.torque.templates.TorqueSchemaAttributeName;
import org.apache.torque.templates.TorqueSchemaElementName;
import org.apache.torque.templates.TorqueSchemaIdMethod;
import org.apache.torque.templates.platform.Platform;
import org.apache.torque.templates.platform.PlatformFactory;
import org.apache.torque.templates.transformer.CollectAttributeSetTrueTransformer;
import org.apache.torque.templates.transformer.IncludeSchemaTransformer;
import org.apache.torque.templates.transformer.LoadExternalSchemaTransformer;
import org.apache.torque.templates.transformer.SchemaTypeHelper;
import org.apache.torque.templates.transformer.om.DatabaseChildElementName;
import org.apache.torque.templates.transformer.om.OMColumnTransformer;
import org.apache.torque.templates.transformer.om.OMTransformer;
import org.apache.torque.templates.transformer.om.TableChildElementName;
import org.apache.torque.templates.typemapping.SchemaType;
import org.apache.torque.templates.typemapping.SqlType;
import org.apache.torque.templates.typemapping.TypeMap;

/**
 * Transforms the tables in the OM model for sql generation.
 */
public class SQLTransformer implements SourceTransformer {
    /** A CollectAttributeSetTrueTransformer instance. */
    private static final CollectAttributeSetTrueTransformer collectAttributeSetTrueTransformer = new CollectAttributeSetTrueTransformer();

    /**
     * The transformer which loads the external schemata.
     *
     * @see LoadExternalSchemaTransformer
     */
    private static final SourceTransformer loadExternalSchemaTransformer = new LoadExternalSchemaTransformer();

    /**
     * The transformer which includes the included schemata.
     *
     * @see LoadExternalSchemaTransformer
     */
    private static final SourceTransformer includeSchemaTransformer = new IncludeSchemaTransformer();

    public SourceElement transform(SourceElement databaseElement, ControllerState controllerState)
            throws SourceTransformerException {
        OMTransformer.setRootDatabaseNameAttribute(databaseElement);
        // include included schemata
        includeSchemaTransformer.transform(databaseElement, controllerState);
        // load referenced external schemata
        loadExternalSchemaTransformer.transform(databaseElement, controllerState);

        TemplateOptionName.checkRequiredOptions(controllerState, TemplateOptionName.DATABASE);

        List<SourceElement> allTables = databaseElement.getChild(DatabaseChildElementName.ALL_TABLES)
                .getChildren(TorqueSchemaElementName.TABLE);
        for (SourceElement tableElement : allTables) {
            transformTable(tableElement, controllerState);
        }
        addDatabaseSchemaElements(databaseElement, controllerState);
        return databaseElement;
    }

    public void transformTable(SourceElement tableElement, ControllerState controllerState)
            throws SourceTransformerException {
        String tableName = (String) tableElement.getAttribute(TorqueSchemaAttributeName.NAME);
        String unqualifiedTableName = tableName;
        if (StringUtils.contains(tableName, ".")) {
            unqualifiedTableName = tableName.substring(tableName.indexOf(".") + 1);
        }
        tableElement.setAttribute(SqlAttributeName.UNQUALIFIED_NAME, unqualifiedTableName);

        Object idMethod = tableElement.getAttribute(TorqueSchemaAttributeName.ID_METHOD.getName());
        if (idMethod == null) {
            Object defaultIdMethod = tableElement.getParent()
                    .getAttribute(TorqueSchemaAttributeName.DEFAULT_ID_METHOD.getName());
            if (defaultIdMethod == null) {
                throw new SourceTransformerException("id Method is not set" + " on table "
                        + tableElement.getAttribute(TorqueSchemaAttributeName.NAME.getName())
                        + " and defaultIdMethod is not set on database");
            }
            tableElement.setAttribute(TorqueSchemaAttributeName.ID_METHOD.getName(), defaultIdMethod);
        }
        if (tableElement.getAttribute(SqlAttributeName.PRIMARY_KEY_CONSTRAINT_NAME) == null) {
            String primaryKeyConstraintName = unqualifiedTableName + "_PK";
            tableElement.setAttribute(SqlAttributeName.PRIMARY_KEY_CONSTRAINT_NAME, primaryKeyConstraintName);
        }
        if (tableElement.getAttribute(SqlAttributeName.SEQUENCE_NAME) == null) {
            String sequenceName = null;
            SourceElement idMethodParameterElement = tableElement
                    .getChild(TorqueSchemaElementName.ID_METHOD_PARAMETER);
            if (idMethodParameterElement != null) {
                sequenceName = (String) idMethodParameterElement.getAttribute(TorqueSchemaAttributeName.VALUE);
            }
            if (StringUtils.isBlank(sequenceName)) {
                sequenceName = tableName + "_SEQ";
            }
            tableElement.setAttribute(SqlAttributeName.SEQUENCE_NAME, sequenceName);
        }

        // primary keys
        collectAttributeSetTrueTransformer.transform(tableElement, controllerState, TorqueSchemaElementName.COLUMN,
                TorqueSchemaAttributeName.PRIMARY_KEY, TableChildElementName.PRIMARY_KEYS);

        StringBuilder primaryKeyColumnNames = new StringBuilder();
        SourceElement primaryKeysElement = tableElement.getChild(TableChildElementName.PRIMARY_KEYS);
        boolean firstPk = true;
        for (SourceElement primaryKeyColumn : primaryKeysElement.getChildren()) {
            if (!firstPk) {
                primaryKeyColumnNames.append(", ");
            }
            primaryKeyColumnNames.append(primaryKeyColumn.getAttribute(TorqueSchemaAttributeName.NAME.getName()));
            firstPk = false;
        }
        tableElement.setAttribute(SqlAttributeName.PRIMARY_KEY_COLUMN_NAMES, primaryKeyColumnNames.toString());

        // unique
        int uniqueIndex = 1;
        for (SourceElement uniqueElement : tableElement.getChildren(TorqueSchemaElementName.UNIQUE.getName())) {
            if (uniqueElement.getAttribute(TorqueSchemaAttributeName.NAME.getName()) == null) {
                uniqueElement.setAttribute(TorqueSchemaAttributeName.NAME.getName(),
                        uniqueElement.getParent().getAttribute(TorqueSchemaAttributeName.NAME.getName()) + "_UQ_"
                                + uniqueIndex);
            }
            String uniqueColumnNames = collectAttributes(uniqueElement,
                    TorqueSchemaElementName.UNIQUE_COLUMN.getName(), TorqueSchemaAttributeName.NAME.getName());
            uniqueElement.setAttribute(SqlAttributeName.UNIQUE_COLUMN_NAMES, uniqueColumnNames);
            ++uniqueIndex;
        }

        // index
        int indexIndex = 1;
        for (SourceElement indexElement : tableElement.getChildren(TorqueSchemaElementName.INDEX.getName())) {
            if (indexElement.getAttribute(TorqueSchemaAttributeName.NAME.getName()) == null) {
                indexElement.setAttribute(TorqueSchemaAttributeName.NAME.getName(),
                        indexElement.getParent().getAttribute(TorqueSchemaAttributeName.NAME.getName()) + "_IDX_"
                                + indexIndex);
            }
            String indexColumnNames = collectAttributes(indexElement,
                    TorqueSchemaElementName.INDEX_COLUMN.getName(), TorqueSchemaAttributeName.NAME.getName());
            indexElement.setAttribute(SqlAttributeName.INDEX_COLUMN_NAMES, indexColumnNames);
            indexIndex++;
        }

        List<SourceElement> columnElements = tableElement.getChildren(TorqueSchemaElementName.COLUMN.getName());
        for (SourceElement columnElement : columnElements) {
            transformColumn(columnElement, controllerState);
        }
        List<SourceElement> foreignKeyElements = tableElement
                .getChildren(TorqueSchemaElementName.FOREIGN_KEY.getName());

        int fkIndex = 1;
        for (SourceElement foreignKeyElemenElement : foreignKeyElements) {
            transformForeignKey(foreignKeyElemenElement, controllerState, fkIndex);
            ++fkIndex;
        }
    }

    /**
     * Enriches the column elements with additional attributes
     * needed for SQL generation.
     *
     * @param columnElement the element to enrich, not null.
     * @param controllerState the current controller state, not null.
     *
     * @throws SourceTransformerException if the name or type attributes
     *         are not set or if the type is unknown.
     */
    private void transformColumn(SourceElement columnElement, ControllerState controllerState)
            throws SourceTransformerException {
        String sql = (String) columnElement.getAttribute(SqlAttributeName.DDL_SQL);
        if (sql == null) {
            sql = getDdlSql(columnElement, controllerState);
            columnElement.setAttribute(SqlAttributeName.DDL_SQL, sql);
        }
    }

    /**
     * Creates the SQL for defining a column.
     *
     * @param columnElement the column element for which the SQL
     *        should be created, not null.
     * @param controllerState the current controller state, not null.
     *
     * @return the SQL for defining the column, not null.
     *
     * @throws SourceTransformerException
     */
    private String getDdlSql(SourceElement columnElement, ControllerState controllerState)
            throws SourceTransformerException {
        SchemaType schemaType = SchemaTypeHelper.getSchemaType(columnElement, controllerState);
        SqlType domainType = SchemaTypeHelper.getDomain(columnElement, controllerState);
        Object size = columnElement.getAttribute(TorqueSchemaAttributeName.SIZE);
        Object scale = columnElement.getAttribute(TorqueSchemaAttributeName.SCALE);
        Object defaultValue = columnElement.getAttribute(TorqueSchemaAttributeName.DEFAULT);
        SqlType sqlType = SchemaTypeHelper.getSqlType(schemaType, domainType, controllerState,
                ObjectUtils.toString(size, null), ObjectUtils.toString(scale, null),
                ObjectUtils.toString(defaultValue, null));
        Platform platform = getPlatform(controllerState);

        List<String> resultList = new ArrayList<String>();

        String sqlTypeName = sqlType.getSqlTypeName();

        if (platform.hasSize(sqlTypeName)) {
            sqlTypeName += sqlType.printSize(platform.getSizeSuffix(sqlTypeName));
        }

        resultList.add(sqlTypeName);

        if (StringUtils.isNotEmpty(sqlType.getDefaultValue())) {
            resultList.add("default");

            if ((SchemaType.DATE == schemaType || SchemaType.TIME == schemaType
                    || SchemaType.TIMESTAMP == schemaType)) {
                if (sqlType.getDefaultValue().startsWith("CURRENT_")) {
                    resultList.add(sqlType.getDefaultValue());
                } else {
                    Date defaultDate = OMColumnTransformer.getDefaultValueAsDate(sqlType.getDefaultValue());
                    if (SchemaType.DATE == schemaType) {
                        resultList.add(platform.getDateString(defaultDate));
                    } else if (SchemaType.TIME == schemaType) {
                        resultList.add(platform.getTimeString(defaultDate));
                    } else {
                        resultList.add(platform.getTimestampString(defaultDate));
                    }
                }
            } else if (TypeMap.isTextType(schemaType)) {
                resultList.add(platform.quoteAndEscape(sqlType.getDefaultValue()));
            } else {
                resultList.add(sqlType.getDefaultValue());
            }
        }

        boolean primaryKey;
        {
            String primaryKeyString = (String) columnElement.getAttribute(TorqueSchemaAttributeName.PRIMARY_KEY);
            primaryKey = Boolean.parseBoolean(primaryKeyString);
        }
        boolean required;
        {
            String requiredString = (String) columnElement.getAttribute(TorqueSchemaAttributeName.REQUIRED);
            required = Boolean.parseBoolean(requiredString);
        }
        boolean isNotNull = primaryKey || required;
        String isNotNullString = platform.getNullString(isNotNull);

        if (platform.createNotNullBeforeAutoincrement() && StringUtils.isNotEmpty(isNotNullString)) {
            resultList.add(isNotNullString);
        }
        // if idMethod was not set explicitly by the user,
        // the transformTable() method sets the attribute from the
        // defaultIdMethod of the database, so this always returns
        // the id method
        Object idMethod = columnElement.getParent().getAttribute(TorqueSchemaAttributeName.ID_METHOD);
        if (primaryKey && TorqueSchemaIdMethod.NATIVE.getName().equals(idMethod)) {
            String autoIncrement = platform.getAutoIncrement();
            if (StringUtils.isNotEmpty(autoIncrement)) {
                resultList.add(autoIncrement);
            }
        }
        if (!platform.createNotNullBeforeAutoincrement() && StringUtils.isNotEmpty(isNotNullString)) {
            resultList.add(isNotNullString);
        }
        return StringUtils.join(resultList.iterator(), ' ');
    }

    private Platform getPlatform(ControllerState controllerState) {
        Platform platform = PlatformFactory
                .getPlatformFor(controllerState.getStringOption(TemplateOptionName.DATABASE));
        return platform;
    }

    /**
     * Sets additional attributes on foreign key elements.
     *
     * @param foreignKeyElement the foreign key element to enrich, not null.
     * @param controllerState the current controller state, not null.
     * @param fkIndex the number of the foreign-key element in the table.
     */
    private void transformForeignKey(SourceElement foreignKeyElement, ControllerState controllerState,
            int fkIndex) {
        if (foreignKeyElement.getAttribute(TorqueSchemaAttributeName.NAME.getName()) == null) {
            foreignKeyElement.setAttribute(TorqueSchemaAttributeName.NAME.getName(),
                    foreignKeyElement.getParent().getAttribute(TorqueSchemaAttributeName.NAME.getName()) + "_FK_"
                            + fkIndex);
        }
        String localColumnsNames = collectAttributes(foreignKeyElement, TorqueSchemaElementName.REFERENCE.getName(),
                TorqueSchemaAttributeName.LOCAL.getName());
        foreignKeyElement.setAttribute(SqlAttributeName.LOCAL_COLUMN_NAMES, localColumnsNames);
        String foreignColumnsNames = collectAttributes(foreignKeyElement,
                TorqueSchemaElementName.REFERENCE.getName(), TorqueSchemaAttributeName.FOREIGN.getName());
        foreignKeyElement.setAttribute(SqlAttributeName.FOREIGN_COLUMN_NAMES, foreignColumnsNames);
    }

    private void addDatabaseSchemaElements(SourceElement databaseElement, ControllerState controllerState) {
        Platform platform = getPlatform(controllerState);
        if (!platform.usesStandaloneSchema()) {
            return;
        }
        List<String> databaseSchemaNames = new ArrayList<String>();
        List<SourceElement> allTables = databaseElement.getChild(DatabaseChildElementName.ALL_TABLES)
                .getChildren(TorqueSchemaElementName.TABLE);
        for (SourceElement tableElement : allTables) {
            String name = (String) tableElement.getAttribute(TorqueSchemaAttributeName.NAME);
            if (StringUtils.contains(name, '.')) {
                String databaseSchema = name.substring(0, name.indexOf('.'));
                if (!databaseSchemaNames.contains(databaseSchema)) {
                    databaseSchemaNames.add(databaseSchema);
                }
            }
        }
        for (String databaseSchemaName : databaseSchemaNames) {
            SourceElement databaseSchemaElement = new SourceElement("databaseSchema");
            databaseSchemaElement.setAttribute("name", databaseSchemaName);
            databaseElement.getChildren().add(databaseSchemaElement);
        }
    }

    /**
     * Collects attribute values in a comma-separated string.
     * The elements on which the attribute values are read can be reached
     * from <code>rootElement</code> via the path <code>sourcePath</code>;
     * and on these elements, the attributes with name
     * <code>attributeName</code> are collected.
     *
     * @param rootElement the base element from where to start, not null.
     * @param sourcePath the path from <code>rootElement</code> to the elements
     *        on which to collect the attributes.
     * @param attributeName the name of the attributes to collect.
     *
     * @return a comma-separated (", ") String containing all the matching
     *         attribute values.
     */
    private String collectAttributes(SourceElement rootElement, String sourcePath, String attributeName) {
        StringBuilder result = new StringBuilder();
        List<SourceElement> toCollectFrom = SourcePath.getElements(rootElement, sourcePath);
        for (SourceElement sourceElement : toCollectFrom) {
            Object attributeValue = sourceElement.getAttribute(attributeName);
            if (attributeValue != null) {
                if (result.length() > 0) {
                    result.append(", ");
                }
                result.append(sourceElement.getAttribute(attributeName));
            }
        }
        return result.toString();
    }
}