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

com.googlecode.paradox.planner.plan.SelectPlan Maven / Gradle / Ivy

There is a newer version: 1.6.3
Show newest version
/*
 * SelectPlan.java 03/14/2009 Copyright (C) 2009 Leonardo Alves da Costa This program is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a
 * copy of the GNU General Public License along with this program. If not, see .
 */
package com.googlecode.paradox.planner.plan;

import com.googlecode.paradox.data.TableData;
import com.googlecode.paradox.data.table.value.FieldValue;
import com.googlecode.paradox.metadata.ParadoxField;
import com.googlecode.paradox.metadata.ParadoxTable;
import com.googlecode.paradox.parser.nodes.SQLNode;
import com.googlecode.paradox.parser.nodes.comparisons.EqualsNode;
import com.googlecode.paradox.parser.nodes.comparisons.GreaterThanNode;
import com.googlecode.paradox.parser.nodes.comparisons.LessThanNode;
import com.googlecode.paradox.parser.nodes.comparisons.NotEqualsNode;
import com.googlecode.paradox.parser.nodes.conditional.ANDNode;
import com.googlecode.paradox.parser.nodes.conditional.ORNode;
import com.googlecode.paradox.planner.nodes.PlanTableNode;
import com.googlecode.paradox.results.Column;
import com.googlecode.paradox.utils.SQLStates;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Creates a SELECT plan for execution.
 *
 * @author Leonardo Alves da Costa
 * @version 1.1
 * @since 1.1
 */
public final class SelectPlan implements Plan {

    /**
     * The columns in this plan.
     */
    private final List columns = new ArrayList<>();

    /**
     * The tables in this plan.
     */
    private final List tables = new ArrayList<>();

    /**
     * The data values.
     */
    private final List> values = new ArrayList<>();

    /**
     * The conditions to filter values
     */
    private final List conditions;

    /**
     * Creates a new instance.
     */
    SelectPlan() {
        this(Collections.emptyList());
    }

    /**
     * Creates a SELECT plan with conditions.
     *
     * @param conditions the conditions to filter results
     */
    public SelectPlan(List conditions) {
        this.conditions = conditions;
    }

    /**
     * Add column from select list.
     *
     * @param name column name.
     * @throws SQLException search column exception.
     */
    public void addColumn(final String name) throws SQLException {
        final ParadoxField field = this.findField(name);
        if (field == null) {
            throw new SQLException(String.format("Invalid column name: '%s'", name),
                    SQLStates.INVALID_COLUMN.getValue());
        }
        this.columns.add(field.getColumn());
    }

    /**
     * Associate all columns from a table.
     *
     * @param table the table to scan.
     * @throws SQLException in case of wrong SQL type.
     */
    public void addColumnFromTable(final ParadoxTable table) throws SQLException {
        for (final ParadoxField field : table.getFields()) {
            this.columns.add(field.getColumn());
        }
    }

    /**
     * Adds a table to this plan.
     *
     * @param table the table.
     */
    public void addTable(final PlanTableNode table) {
        this.tables.add(table);
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void execute() throws SQLException {
        if (this.columns.isEmpty() || this.tables.isEmpty()) {
            return;
        }

        for (final Column column : this.columns) {
            for (final PlanTableNode table : this.tables) {
                final ParadoxTable pTable = table.getTable();
                if (column.getTableName().equalsIgnoreCase(pTable.getName())) {
                    this.loadTableData(column, pTable);
                }
            }
        }
    }

    /**
     * Gets the columns in SELECT statement.
     *
     * @return the columns in SELECT statement.
     */
    public List getColumns() {
        return Collections.unmodifiableList(this.columns);
    }

    /**
     * Gets the tables in this plan.
     *
     * @return the tables in this plan.
     */
    public List getTables() {
        return Collections.unmodifiableList(this.tables);
    }

    /**
     * Values from tables in column order.
     *
     * @return array of array of values/ Can be null (empty result set);
     */
    public List> getValues() {
        return Collections.unmodifiableList(this.values);
    }

    /**
     * Fill the result row with a field order and filters the result row by the
     * conditions.
     *
     * @param tableData  the table data load from.
     * @param fieldOrder the field order.
     * @throws SQLException in case of errors.
     */
    private void fillResultValues(final List> tableData, final int fieldOrder) throws SQLException {
        int placeholder = 0;
        boolean addingAtt = !this.values.isEmpty();

        for (int j = 0; j < tableData.size(); j++) {
            List resultRow;
            if (conditions.isEmpty()) {
                if (j == this.values.size()) {
                    resultRow = new ArrayList<>();
                    this.values.add(resultRow);
                } else {
                    resultRow = this.values.get(j);
                }
                resultRow.add(tableData.get(j).get(fieldOrder));
            } else {
                if (checkConditions(0, tableData.get(j))) {
                    resultRow = new ArrayList<>();
                    if (addingAtt) {
                        resultRow = this.values.get(placeholder++);
                    } else {
                        this.values.add(resultRow);
                    }
                    resultRow.add(tableData.get(j).get(fieldOrder));
                }
            }
        }
    }

    /**
     * Check the conditions by concatenating and evaluating the comparison nodes.
     *
     * @param numCondition quantity of conditions to be verified.
     * @param listField    list of fields.
     * @throws SQLException in case of errors.
     */
    private boolean checkConditions(int numCondition, List listField) throws SQLException {
        if (numCondition == conditions.size() - 1) {
            return evaluateCondition(conditions.get(numCondition), listField);
        } else if (conditions.get(numCondition + 1) instanceof ANDNode) {
            return evaluateCondition(conditions.get(numCondition), listField) && checkConditions(numCondition + 2,
                    listField);
        } else if (conditions.get(numCondition + 1) instanceof ORNode) {
            return evaluateCondition(conditions.get(numCondition), listField) || checkConditions(numCondition + 2,
                    listField);
        } else {
            return false;
        }
    }

    /**
     * Evaluate the conditions.
     *
     * @param condition the condition to be evaluated.
     * @param listField list of fields.
     * @throws SQLException in case of erros.
     */
    private boolean evaluateCondition(SQLNode condition, List listField) throws SQLException {
        if (condition instanceof EqualsNode) {
            EqualsNode nodeCondition = (EqualsNode) condition;
            int fieldNumOrder = findField(nodeCondition.getFirst().toString()).getOrderNum() - 1;
            FieldValue column = listField.get(fieldNumOrder);
            return nodeCondition.getFirst().toString().equalsIgnoreCase(column.getField().toString())
                    && nodeCondition.getLast().toString().equalsIgnoreCase(column.getValue().toString().toUpperCase());

        } else if (condition instanceof NotEqualsNode) {
            NotEqualsNode nodeCondition = (NotEqualsNode) condition;
            int fieldNumOrder = findField(nodeCondition.getFirst().toString()).getOrderNum() - 1;
            FieldValue column = listField.get(fieldNumOrder);
            return nodeCondition.getFirst().toString().equalsIgnoreCase(column.getField().toString())
                    && !nodeCondition.getLast().toString().equalsIgnoreCase(
                    column.getValue().toString());
        } else if (condition instanceof GreaterThanNode) {
            GreaterThanNode nodeCondition = (GreaterThanNode) condition;
            int fieldNumOrder = findField(nodeCondition.getFirst().toString()).getOrderNum() - 1;
            FieldValue column = listField.get(fieldNumOrder);
            return nodeCondition.getFirst().toString().equalsIgnoreCase(column.getField().toString())
                    && Double.parseDouble(column.getValue().toString()) > Double.parseDouble(
                    nodeCondition.getLast().toString());
        } else if (condition instanceof LessThanNode) {
            LessThanNode nodeCondition = (LessThanNode) condition;
            int fieldNumOrder = findField(nodeCondition.getFirst().toString()).getOrderNum() - 1;
            FieldValue column = listField.get(fieldNumOrder);
            return nodeCondition.getFirst().toString().equalsIgnoreCase(column.getField().toString())
                    && Double.parseDouble(column.getValue().toString()) < Double.parseDouble(
                    nodeCondition.getLast().toString());
        }

        return false;
    }

    /**
     * Finds a single column in the table list.
     *
     * @param fieldName the field name.
     * @param fields    the field list.
     * @param prefix    the field prefix.
     * @throws SQLException in case of parse errors.
     */
    private void findColumn(final String fieldName, final List fields, final String prefix)
    throws SQLException {
        for (final PlanTableNode table : this.tables) {
            if (table.getTable() == null) {
                throw new SQLException("Empty table", SQLStates.INVALID_TABLE.getValue());
            }

            if ((prefix != null) && (table.getAlias() != null) && !prefix.equalsIgnoreCase(table.getAlias())) {
                continue;
            }

            for (final ParadoxField field : table.getTable().getFields()) {
                if (field.getName().equalsIgnoreCase(fieldName)) {
                    fields.add(field);
                    // Unique column in table
                    break;
                }
            }
        }
    }

    /**
     * Find a paradox field by its name.
     *
     * @param name the field name.
     * @return the paradox field.
     * @throws SQLException in case of find errors.
     */
    private ParadoxField findField(final String name) throws SQLException {
        String newName = name;
        final List fields = new ArrayList<>(1);
        String prefix = null;
        final int p = newName.indexOf('.');
        if (p > -1) {
            prefix = newName.substring(0, p);
            newName = newName.substring(p + 1);
        }

        this.findColumn(newName, fields, prefix);
        if (!fields.isEmpty()) {
            if (fields.size() > 1) {
                throw new SQLException("Column '" + newName + "' ambiguously defined",
                        SQLStates.COLUMN_AMBIQUOUS.getValue());
            } else {
                return fields.get(0);
            }
        }

        throw new SQLException(String.format("Field %s not found.", name));
    }

    /**
     * Load the table data form a table.
     *
     * @param column the column to load.
     * @param table  the table to load.
     * @throws SQLException in case of execution errors.
     */
    private void loadTableData(final Column column, final ParadoxTable table) throws SQLException {
        final ParadoxField field = table.findField(column.getName());
        if (field == null) {
            throw new SQLException("Column '" + column.getName() + "' not found in table '" + table.getName(),
                    SQLStates.INVALID_FIELD_VALUE.getValue());
        }
        // load table data
        final List> tableData = TableData.loadData(table, table.getFields());
        // search column index
        if ((field.getOrderNum() > table.getFields().size()) || (field.getOrderNum() < 1)) {
            throw new SQLException("Invalid column position", SQLStates.INVALID_FIELD_VALUE.getValue());
        }

        final int p = field.getOrderNum() - 1;
        this.fillResultValues(tableData, p);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy