com.iksgmbh.sql.pojomemodb.sqlparser.CreateTableParser.java Source code

Java tutorial

Introduction

Here is the source code for com.iksgmbh.sql.pojomemodb.sqlparser.CreateTableParser.java

Source

/*
 * Copyright 2016 IKS Gesellschaft fuer Informations- und Kommunikationssysteme mbH
 * 
 * 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.
 */
package com.iksgmbh.sql.pojomemodb.sqlparser;

import com.iksgmbh.sql.pojomemodb.SQLKeyWords;
import com.iksgmbh.sql.pojomemodb.SqlPojoMemoDB;
import com.iksgmbh.sql.pojomemodb.dataobjects.interfaces.metadata.TableMetaData;
import com.iksgmbh.sql.pojomemodb.dataobjects.persistent.Column;
import com.iksgmbh.sql.pojomemodb.dataobjects.persistent.Table;
import com.iksgmbh.sql.pojomemodb.dataobjects.temporal.ColumnInitData;
import com.iksgmbh.sql.pojomemodb.utils.StringParseUtil;
import com.iksgmbh.sql.pojomemodb.utils.StringParseUtil.*;
import org.apache.commons.lang3.StringUtils;

import java.sql.SQLDataException;
import java.sql.SQLException;

import static com.iksgmbh.sql.pojomemodb.SQLKeyWords.*;
import static com.iksgmbh.sql.pojomemodb.utils.StringParseUtil.*;

public class CreateTableParser extends SqlPojoMemoParser {
    private static final String[] EndOfColumnInfoKeyWords = { CONSTRAINT, USING_INDEX };

    public CreateTableParser(final SqlPojoMemoDB memoryDb) {
        this.memoryDb = memoryDb;
    }

    /**
     * Defines the SQL command whose SQL statement is parsed by this parser
     */
    @Override
    protected String getSqlCommand() {
        return SQLKeyWords.CREATE_TABLE_COMMAND.toLowerCase();
    }

    public TableMetaData parseCreateTableStatement(final String sql) throws SQLException {
        final InterimParseResult parseResult = parseTableName(sql);
        final String tableName = removeSurroundingQuotes(parseResult.parsedValue);
        final TableMetaData tableMetaData = new Table(tableName);

        if (!parseResult.unparsedRest.startsWith("(")) {
            throw new SQLException("Left parenthis missing...");
        }

        int numOpen = StringParseUtil.countOccurrencesOf(parseResult.unparsedRest, OPENING_PARENTHESIS.charAt(0));
        int numClose = StringParseUtil.countOccurrencesOf(parseResult.unparsedRest, CLOSING_PARENTHESIS.charAt(0));
        if (numOpen > numClose) {
            throw new SQLException("Missing closing parenthesis in '" + parseResult.unparsedRest + "'.");
        }

        processColumnData(parseResult.unparsedRest.substring(1, parseResult.unparsedRest.length() - 1),
                tableMetaData);

        return tableMetaData;
    }

    /**
     * Parses column data and creates new columns in tableMetaData
     *
     * @param unparsedRest
     * @param tableMetaData
     * @return
     * @throws SQLException
     */
    private String processColumnData(String unparsedRest, final TableMetaData tableMetaData) throws SQLException {
        while (!isEndOfColumnDataReached(unparsedRest)) {
            if (unparsedRest.toUpperCase().startsWith(PRIMARY_KEY.toUpperCase())) {
                unparsedRest = createDefaultPrimaryConstraint(unparsedRest, tableMetaData);
            } else if (unparsedRest.toUpperCase().startsWith(UNIQUE.toUpperCase())) {
                unparsedRest = createDefaultUniqueConstraint(unparsedRest, tableMetaData);
            } else if (unparsedRest.toUpperCase().startsWith(CONSTRAINT.toUpperCase())) {

                if (unparsedRest.toUpperCase().contains(PRIMARY_KEY.toUpperCase())) {
                    unparsedRest = createPrimaryConstraint(unparsedRest, tableMetaData);
                }
                if (unparsedRest.toUpperCase().contains(UNIQUE.toUpperCase())) {
                    unparsedRest = createUniqueConstraint(unparsedRest, tableMetaData);
                }
            } else {
                unparsedRest = processColumnInformation(unparsedRest.trim(), tableMetaData);
            }
        }

        return unparsedRest;
    }

    private String createPrimaryConstraint(String unparsedRest, TableMetaData tableMetaData) throws SQLException {
        String toReturn = unparsedRest.substring(CONSTRAINT.length()).trim();
        InterimParseResult parseResult = parseNextValue(toReturn, PRIMARY_KEY);

        if (StringUtils.isEmpty(parseResult.delimiter)) {
            throw new SQLException("Cannot parse to Primary Key Constraint: " + unparsedRest
                    + " Expected something like 'CONSTRAINT PRIMARY_KEY_ID PRIMARY KEY (COLUMN_NAME)'.");
        }

        String primaryKeyId = removeSurroundingQuotes(parseResult.parsedValue);
        parseResult = parseNextValue(parseResult.unparsedRest, COMMA);
        String constraintColumnName = null;

        if (parseResult.delimiter == null) {
            toReturn = "";
            constraintColumnName = parseResult.parsedValue;
        } else {
            toReturn = parseResult.unparsedRest;
            constraintColumnName = parseResult.parsedValue;

            int pos = constraintColumnName.toUpperCase().indexOf(USING_INDEX.toUpperCase());
            if (pos > 0) {
                constraintColumnName = constraintColumnName.substring(0, pos).trim();
            }
        }

        if (constraintColumnName.startsWith(OPENING_PARENTHESIS)) {
            if (constraintColumnName.endsWith(CLOSING_PARENTHESIS)) {
                constraintColumnName = constraintColumnName.substring(1, constraintColumnName.length() - 1);
            } else {
                throw new SQLException("Cannot parse to primary key constraint: " + unparsedRest
                        + ". Expected something like 'CONSTRAINT PRIMARY_KEY_ID PRIMARY KEY (COLUMN_NAME)'.");
            }
        } else {
            if (constraintColumnName.endsWith(CLOSING_PARENTHESIS)) {
                throw new SQLException("Cannot parse to primary key constraint: " + unparsedRest
                        + ". Expected something like 'CONSTRAINT PRIMARY_KEY_ID PRIMARY KEY (COLUMN_NAME)'.");
            }
        }

        constraintColumnName = removeSurroundingQuotes(constraintColumnName);
        Column column = ((Table) tableMetaData).getColumn(constraintColumnName);
        column.setPrimaryKeyId(primaryKeyId);
        column.setNullable(false); // Primary Key column must not be nullable

        return toReturn;
    }

    private String createUniqueConstraint(String unparsedRest, TableMetaData tableMetaData) throws SQLException {
        String toReturn = unparsedRest.substring(CONSTRAINT.length()).trim();
        InterimParseResult parseResult = parseNextValue(toReturn, SPACE + UNIQUE);

        if (StringUtils.isEmpty(parseResult.delimiter)) {
            throw new SQLException("Cannot parse to Primary Key Constraint: " + unparsedRest
                    + " Expected something like 'CONSTRAINT UNIQUE_ID UNIQUE (COLUMN_NAME)'.");
        }

        String uniqueConstraintId = removeSurroundingQuotes(parseResult.parsedValue);
        parseResult = parseNextValue(parseResult.unparsedRest, CLOSING_PARENTHESIS);
        String constraintColumnName = null;

        if (parseResult.unparsedRest == null) {
            toReturn = "";
            constraintColumnName = parseResult.parsedValue + CLOSING_PARENTHESIS;
        } else {
            toReturn = parseResult.unparsedRest.trim();
            constraintColumnName = parseResult.parsedValue + CLOSING_PARENTHESIS;

            int pos = constraintColumnName.toUpperCase().indexOf(USING_INDEX.toUpperCase());
            if (pos > 0) {
                constraintColumnName = constraintColumnName.substring(0, pos).trim();
            }
        }

        if (constraintColumnName.startsWith(OPENING_PARENTHESIS)) {
            if (constraintColumnName.endsWith(CLOSING_PARENTHESIS)) {
                constraintColumnName = constraintColumnName.substring(1, constraintColumnName.length() - 1);
            } else {
                throw new SQLException("Cannot parse to primary key constraint: " + unparsedRest
                        + ". Expected something like 'CONSTRAINT UNIQUE_ID UNIQUE KEY (COLUMN_NAME)'.");
            }
        } else {
            if (constraintColumnName.endsWith(CLOSING_PARENTHESIS)) {
                throw new SQLException("Cannot parse to primary key constraint: " + unparsedRest
                        + ". Expected something like 'CONSTRAINT UNIQUE UNIQUE (COLUMN_NAME)'.");
            }
        }

        constraintColumnName = removeSurroundingQuotes(constraintColumnName);
        Column column = ((Table) tableMetaData).getColumn(constraintColumnName);
        column.setUniqueConstraintId(uniqueConstraintId);

        if (toReturn.startsWith(COMMA))
            toReturn = toReturn.substring(1);

        return toReturn.trim();
    }

    private String createDefaultPrimaryConstraint(String unparsedRest, TableMetaData tableMetaData)
            throws SQLException {
        String toReturn = unparsedRest.substring(PRIMARY_KEY.length()).trim();
        if (!toReturn.startsWith(OPENING_PARENTHESIS)) {
            throw new SQLException("Cannot parse to primary key constraint: " + unparsedRest
                    + ". Expected something like 'PRIMARY KEY (COLUMN_NAME)'.");
        }

        int pos = toReturn.indexOf(CLOSING_PARENTHESIS);

        String constraintColumnName = toReturn.substring(1, pos);

        Column column = ((Table) tableMetaData).getColumn(constraintColumnName);
        column.setPrimaryKeyId(createDefaultPrimaryConstraintName(constraintColumnName));
        column.setNullable(false); // Primary Key column must not be nullable

        return toReturn.substring(pos).trim();
    }

    private String createDefaultUniqueConstraint(String unparsedRest, TableMetaData tableMetaData)
            throws SQLException {
        String toReturn = unparsedRest.substring(UNIQUE.length()).trim();
        if (!toReturn.startsWith(OPENING_PARENTHESIS)) {
            throw new SQLException("Cannot parse to unique constraint: " + unparsedRest
                    + ". Expected something like 'UNIQUE (COLUMN_NAME)'.");
        }

        int pos = toReturn.indexOf(CLOSING_PARENTHESIS);

        String constraintColumnName = toReturn.substring(1, pos);

        Column column = ((Table) tableMetaData).getColumn(constraintColumnName);
        column.setUniqueConstraintId(createDefaultUniqueConstraintName(constraintColumnName));

        return toReturn.substring(pos).trim();
    }

    private boolean isEndOfColumnDataReached(final String unparsedRest) {
        if (unparsedRest.length() == 0 || unparsedRest.startsWith(CLOSING_PARENTHESIS)) {
            return true;
        }

        if (unparsedRest.contains(PRIMARY_KEY) || unparsedRest.contains(UNIQUE)) {
            return false;
        }

        for (String keyword : EndOfColumnInfoKeyWords) {
            if (unparsedRest.toLowerCase().startsWith(keyword.toLowerCase())) {
                return true;
            }
        }

        return false;
    }

    /**
     * Parses column data and creates a new column in tableMetaData
     *
     * @param columnInformation
     * @param tableMetaData
     * @return
     * @throws SQLException
     */
    private String processColumnInformation(final String columnInformation, final TableMetaData tableMetaData)
            throws SQLException {
        // parse column name
        InterimParseResult parseResult = parseNextValue(columnInformation, SPACE);
        if (parseResult.parsedValue == null) {
            throw new SQLException("Column name not found but expected");
        }
        final ColumnInitData columnInitData = new ColumnInitData(removeSurroundingQuotes(parseResult.parsedValue));

        // parse column type
        parseResult = parseNextValue(parseResult.unparsedRest, OPENING_PARENTHESIS.charAt(0),
                CLOSING_PARENTHESIS.charAt(0), COMMA.charAt(0));
        if (parseResult.parsedValue == null) {
            throw new SQLException("Column type not found but expected");
        }
        columnInitData.columnType = correctParenthesisIfNecessary(parseResult.parsedValue);
        String toReturn = parseResult.unparsedRest;

        parseNullableConstraint(columnInitData);
        parseDefaultValue(columnInitData);
        parseUniqueConstraint(columnInitData); // call this parse method in the end !
        parsePrimaryKeyConstraint(columnInitData); // call this parse method in the end !

        // collect parse results
        tableMetaData.createNewColumn(columnInitData, memoryDb);

        return toReturn;
    }

    private void parseUniqueConstraint(ColumnInitData columnInitData) {
        int pos = columnInitData.columnType.toUpperCase().indexOf(SQLKeyWords.UNIQUE.toUpperCase());
        if (pos > -1) {
            columnInitData.columnType = (columnInitData.columnType.substring(0, pos)
                    + columnInitData.columnType.substring(pos + SQLKeyWords.UNIQUE.length())).trim();
            columnInitData.uniqueKey = createDefaultUniqueConstraintName(columnInitData.columnName);
        }
    }

    private void parsePrimaryKeyConstraint(ColumnInitData columnInitData) throws SQLException {
        int pos = columnInitData.columnType.toUpperCase().indexOf(SQLKeyWords.PRIMARY_KEY.toUpperCase());
        if (pos > -1) {
            columnInitData.nullable = false; // primary key must not be null !
            columnInitData.columnType = (columnInitData.columnType.substring(0, pos)
                    + columnInitData.columnType.substring(pos + SQLKeyWords.PRIMARY_KEY.length())).trim();
            columnInitData.primaryKey = createDefaultPrimaryConstraintName(columnInitData.columnName);
        }

        if (!StringUtils.isEmpty(columnInitData.primaryKey) && columnInitData.nullable) {
            throw new SQLException(
                    "Primary Key column '" + columnInitData.columnName + "' must not be set to nullable=true!");
        }
    }

    private String createDefaultPrimaryConstraintName(String columnName) {
        return "PK_" + columnName;
    }

    private String createDefaultUniqueConstraintName(String columnName) {
        return "UC_" + columnName;
    }

    private void parseNullableConstraint(ColumnInitData columnInitData) {
        int pos = columnInitData.columnType.toUpperCase().indexOf(SQLKeyWords.NOT_NULL_ENABLED.toUpperCase());
        if (pos > -1) {
            columnInitData.nullable = false;
            columnInitData.columnType = columnInitData.columnType.substring(0, pos)
                    + columnInitData.columnType.substring(pos + SQLKeyWords.NOT_NULL_ENABLED.length());
        } else {
            pos = columnInitData.columnType.toUpperCase().indexOf(SQLKeyWords.NOT_NULL.toUpperCase());
            if (pos > -1) {
                columnInitData.nullable = false;
                columnInitData.columnType = columnInitData.columnType.substring(0, pos)
                        + columnInitData.columnType.substring(pos + SQLKeyWords.NOT_NULL.length());
            }
        }

        columnInitData.columnType = columnInitData.columnType.trim();
    }

    private void parseDefaultValue(ColumnInitData columnInitData) throws SQLException {
        if (columnInitData.columnType.toUpperCase().contains(SQLKeyWords.DEFAULT.toUpperCase())) {
            if (columnInitData.columnType.toUpperCase().contains(SQLKeyWords.PRIMARY_KEY.toUpperCase())) {
                throw new SQLException(
                        "Primary Key column '" + columnInitData.columnName + "' must not define a default value!");
            }

            InterimParseResult parseResult = parseNextValue(columnInitData.columnType, SQLKeyWords.DEFAULT);
            columnInitData.defaultValue = parseResult.unparsedRest;
            columnInitData.columnType = parseResult.parsedValue;
            if (StringUtils.isEmpty(columnInitData.defaultValue) && !StringUtils.isEmpty(parseResult.delimiter))
                throw new SQLDataException("Missing default value for column '" + columnInitData.columnName + "'!");
        }
    }

    private String correctParenthesisIfNecessary(final String columnType) {
        final int numOpenP = StringParseUtil.countOccurrencesOf(columnType, OPENING_PARENTHESIS.charAt(0));
        final int numCloseP = StringParseUtil.countOccurrencesOf(columnType, CLOSING_PARENTHESIS.charAt(0));

        if (numOpenP > numCloseP) {
            return columnType + CLOSING_PARENTHESIS;
        }

        if (numOpenP < numCloseP) {
            return columnType.substring(0, columnType.length() - 1);
        }

        return columnType;
    }
}