Package com.scooterframework.orm.sqldataexpress.service

Source Code of com.scooterframework.orm.sqldataexpress.service.SqlServiceImpl

/*
*   This software is distributed under the terms of the FSF
*   Gnu Lesser General Public License (see lgpl.txt).
*
*   This program is distributed WITHOUT ANY WARRANTY. See the
*   GNU General Public License for more details.
*/
package com.scooterframework.orm.sqldataexpress.service;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.PooledDataSource;
import com.scooterframework.common.logging.LogUtil;
import com.scooterframework.common.util.StringUtil;
import com.scooterframework.common.util.Util;
import com.scooterframework.orm.activerecord.ActiveRecordConstants;
import com.scooterframework.orm.sqldataexpress.config.DatabaseConfig;
import com.scooterframework.orm.sqldataexpress.config.SqlConfig;
import com.scooterframework.orm.sqldataexpress.connection.DatabaseConnectionContext;
import com.scooterframework.orm.sqldataexpress.connection.UserDatabaseConnection;
import com.scooterframework.orm.sqldataexpress.exception.BaseSQLException;
import com.scooterframework.orm.sqldataexpress.exception.CreateConnectionFailureException;
import com.scooterframework.orm.sqldataexpress.exception.TransactionException;
import com.scooterframework.orm.sqldataexpress.exception.UnexpectedDataException;
import com.scooterframework.orm.sqldataexpress.exception.UnsupportedDataProcessorNameException;
import com.scooterframework.orm.sqldataexpress.exception.UnsupportedDataProcessorTypeException;
import com.scooterframework.orm.sqldataexpress.exception.UnsupportedStoredProcedureAPINameException;
import com.scooterframework.orm.sqldataexpress.object.OmniDTO;
import com.scooterframework.orm.sqldataexpress.object.RowData;
import com.scooterframework.orm.sqldataexpress.object.RowInfo;
import com.scooterframework.orm.sqldataexpress.object.TableData;
import com.scooterframework.orm.sqldataexpress.processor.DataProcessor;
import com.scooterframework.orm.sqldataexpress.processor.DataProcessorFactory;
import com.scooterframework.orm.sqldataexpress.processor.DataProcessorTypes;
import com.scooterframework.transaction.ImplicitTransactionManager;
import com.scooterframework.transaction.Transaction;
import com.scooterframework.transaction.TransactionManager;
import com.scooterframework.transaction.TransactionManagerUtil;

/**
* SqlServiceImpl class implements SqlService interface.
*
* @author (Fei) John Chen
*/
public class SqlServiceImpl implements SqlService {
    public SqlServiceImpl() {}


    /**
     * Begin a transaction of default transaction type.
     *
     * The default transaction type is JDBC if it is not defined in property file.
     */
    public void beginTransaction()
    throws TransactionException {
        getTransactionManager().beginTransaction();
    }

    /**
     * Begin a transaction of a specific type.
     */
    public void beginTransaction(String type)
    throws TransactionException {
        getTransactionManager().beginTransaction(type);
    }

    /**
     * Commit a transaction.
     */
    public void commitTransaction()
    throws TransactionException {
        getTransactionManager().commitTransaction();
    }

    /**
     * Rollback a transaction.
     */
    public void rollbackTransaction()
    throws TransactionException {
        getTransactionManager().rollbackTransaction();
    }

    /**
     * Release all resources hold by this transaction.
     */
    public void releaseResources()
    throws TransactionException {
        getTransactionManager().releaseResources();
    }

    /**
     * execute without output filter
     */
    public OmniDTO execute(Map<String, Object> inputs, String processorType, String processorName)
    throws BaseSQLException {
        return execute(inputs, processorType, processorName, new HashMap<String, String>());
    }

    /**
     * execute with output filter
     */
    public OmniDTO execute(Map<String, Object> inputs, String processorType, String processorName, Map<String, String> outputFilters)
    throws BaseSQLException {
        if (processorType == null || processorName == null)
            throw new IllegalArgumentException("processorType or processorName is null.");

        if (inputs == null) inputs = new HashMap<String, Object>();

        ImplicitTransactionManager tm = TransactionManagerUtil.getImplicitTransactionManager();
        OmniDTO returnTO = null;

        try {
            tm.beginTransactionImplicit();

            //if (log.isDebugEnabled()) displayDS((String)inputs.get(DataProcessor.input_key_database_connection_name), "beforeConnection");

            UserDatabaseConnection connection = findOrCreateConnection(inputs);

            //if (log.isDebugEnabled()) displayDS((String)inputs.get(DataProcessor.input_key_database_connection_name), "beforeExecute");
            returnTO = executeKeepConnection(connection, inputs, processorType, processorName, outputFilters);
            //if (log.isDebugEnabled()) displayDS((String)inputs.get(DataProcessor.input_key_database_connection_name), "afterExecute");

            tm.commitTransactionImplicit();
            //if (log.isDebugEnabled()) displayDS((String)inputs.get(DataProcessor.input_key_database_connection_name), "afterCommit");
        }
        catch(BaseSQLException bdex) {
            tm.rollbackTransactionImplicit();
            throw bdex;
        }
        finally {
            tm.releaseResourcesImplicit();
            displayDS((String)inputs.get(DataProcessor.input_key_database_connection_name), "afterRelease");
        }

        return returnTO;
    }

  private void displayDS(String connectionName, String where) {
        try {
            DataSource ds = null;
            if (connectionName == null) {
                ds = (DataSource) DatabaseConfig.getInstance().getPooledDataSource();
            }
            else {
                ds = (DataSource) DatabaseConfig.getInstance().getPooledDataSource(connectionName);
            }

            // make sure it's a c3p0 PooledDataSource
            if (ds != null && (ds instanceof PooledDataSource)) {
                PooledDataSource pds = (PooledDataSource) ds;
                log.debug("displayDS for " + where + " -      num_connections: " + pds.getNumConnectionsDefaultUser());
                log.debug("displayDS for " + where + " - num_busy_connections: " + pds.getNumBusyConnectionsDefaultUser());
                log.debug("displayDS for " + where + " - num_idle_connections: " + pds.getNumIdleConnectionsDefaultUser());
            }
        } catch(Exception ex) {
            log.debug("displayDS for " + where + " - ERROR: " + ex.getMessage());
        }
    }

    /**
     * execute a collection of InputInfo objects in one transaction
     */
    public Collection<OmniDTO> execute(Collection<InputInfo> inputInfoList)
    throws BaseSQLException {
        if (inputInfoList == null)
            throw new IllegalArgumentException("inputs list is null.");

        ImplicitTransactionManager tm = TransactionManagerUtil.getImplicitTransactionManager();
        Collection<OmniDTO> returnTOList = new ArrayList<OmniDTO>();

        try {
            tm.beginTransactionImplicit();

            for (InputInfo ip : inputInfoList) {
                UserDatabaseConnection connection = findOrCreateConnection(ip);
                OmniDTO returnTO = executeKeepConnection(connection, ip.getInputs(), ip.getProcessorType(), ip.getProcessorName(), ip.getOutputFilters());
                returnTOList.add(returnTO);

                //now execute child InputInfo
                Collection<InputInfo> childList = ip.getChildInputInfoObjects();
                for (InputInfo childIp : childList) {
                    OmniDTO returnTO2 = executeKeepConnection(connection, childIp.getInputs(), childIp.getProcessorType(), childIp.getProcessorName(), childIp.getOutputFilters());
                    returnTO.addChildrenOmniDTOToList(returnTO2);
                }
            }

            tm.commitTransactionImplicit();
        }
        catch(BaseSQLException bdex) {
            tm.rollbackTransactionImplicit();
            throw bdex;
        }
        finally {
            tm.releaseResourcesImplicit();
        }

        return returnTOList;
    }

    /**
     * execute a collection of InputInfo objects in one transaction
     */
    public OmniDTO retrieveMasterDetails(InputInfo inputInfo)
    throws BaseSQLException {
        if (inputInfo == null)
            throw new IllegalArgumentException("inputInfo is null.");

        ImplicitTransactionManager tm = TransactionManagerUtil.getImplicitTransactionManager();
        OmniDTO returnTO = null;

        try {
            tm.beginTransactionImplicit();

            InputInfo ip = inputInfo;
            UserDatabaseConnection udc = findOrCreateConnection(ip);
            udc.getConnection().setReadOnly(true);

            returnTO = executeKeepConnection(udc, ip.getInputs(), ip.getProcessorType(), ip.getProcessorName(), ip.getOutputFilters());
            log.debug("parent: " + returnTO);

            //now execute child InputInfo
            Collection<InputInfo> childList = ip.getChildInputInfoObjects();
            for (InputInfo childIp : childList) {
                // find all input parameters in childIp that need data from parent
                List<String> connectorList = new ArrayList<String>();
                Map<String, Object> childInputs = childIp.getInputs();
                childInputs = convertKeyCase(childInputs);
                for (Map.Entry<String, Object> entry : childInputs.entrySet()) {
                    String key = entry.getKey();
                    String value = (String)childInputs.get(key);
                    if (key != null && key.startsWith("&")) connectorList.add(value);
                }

                // create a select union query
                String query = null;
                if (DataProcessorTypes.NAMED_SQL_STATEMENT_PROCESSOR.equals(childIp.getProcessorType())) {
                    query = SqlConfig.getInstance().getSql(childIp.getProcessorName());
                }
                else
                if (DataProcessorTypes.DIRECT_SQL_STATEMENT_PROCESSOR.equals(childIp.getProcessorType())) {
                    query = childIp.getProcessorName();
                }

                log.debug("child query1: " + query);

                // check if parent has data
                boolean parentHasData = false;
                TableData parentRt = null;
                if (returnTO != null) {
                    parentRt = returnTO.getTableData(ip.getProcessorName());
                    if (parentRt != null) {
                        int size = parentRt.getAllRows().size();
                        if (size > 0) parentHasData = true;
                    }
                }

                // construct child query
                String childQuery = "";
                if (query != null && connectorList.size() > 0 && parentHasData) {
                    childQuery = getNewChildQuery(query, childIp, parentRt.getAllRows());
                }
                else {
                    childQuery = query;
                }

                log.debug("child query2: " + childQuery);

                if (parentHasData) {
                    udc = findOrCreateConnection(childIp);

                    OmniDTO returnTO2 =
                        executeKeepConnection(udc,
                                              childIp.getInputs(),
                                              DataProcessorTypes.DIRECT_SQL_STATEMENT_PROCESSOR,
                                              childQuery,
                                              childIp.getOutputFilters());

                    // merge child records with corresponding parent record
                    if (returnTO2 != null) {
                        linkParentWithChild(parentRt,
                                            returnTO2.getTableData(childQuery),
                                            childIp.getProcessorName(),
                                            connectorList);
                    }

                    log.debug("returnTO2: " + returnTO2);
                }
            }
        }
        catch(SQLException ex) {
            throw new BaseSQLException(ex);
        }
        catch(BaseSQLException bdex) {
            throw bdex;
        }
        finally {
            tm.releaseResourcesImplicit();
        }

        return returnTO;
    }

    /**
     * execute
     */
    public OmniDTO execute(Collection<InputParameter> inputParameters, String processorType, String processorName)
    throws BaseSQLException {
        return execute(inputParameters, processorType, processorName, null);
    }

    /**
     * execute of InputInfo with output filter
     */
    public OmniDTO execute(Collection<InputParameter> inputParameters, String processorType, String processorName, Map<String, String> outputFilters)
    throws BaseSQLException {
        if (inputParameters == null) throw new IllegalArgumentException("inputs is null.");

        Map<String, Object> inputsMap = new HashMap<String, Object>();

        for (InputParameter ip : inputParameters) {
            inputsMap.put(ip.getName(), ip.getValue());
        }

        return execute(inputsMap, processorType, processorName, outputFilters);
    }


    /**
     * Retrieve a single row data from database. If more than one records is
     * returned, an UnexpectedDataException will be thrown.
     *
     * @param inputs            Map of input data
     * @param processorType     A named SQL or direct SQL or stored procedure
     * @param processorName     SQL name or SQL itself or stored procedure name
     * @return TableData        The row data
     * @throws com.scooterframework.orm.sqldataexpress.exception.BaseSQLException
     */
    public TableData retrieveRow(Map<String, Object> inputs, String processorType, String processorName)
    throws BaseSQLException {
      inputs.put(DataProcessor.input_key_records_fixed, true);
        return retrieveRows(inputs, processorType, processorName, 1);
    }

    /**
     * Retrieve a list of rows from database.
     *
     * @param inputs            Map of input data
     * @param processorType     A named SQL or direct SQL or stored procedure
     * @param processorName     SQL name or SQL itself or stored procedure name
     * @return TableData        The list of row data
     * @throws com.scooterframework.orm.sqldataexpress.exception.BaseSQLException
     */
    public TableData retrieveRows(Map<String, Object> inputs, String processorType, String processorName)
    throws BaseSQLException {
        return retrieveRows(inputs, processorType, processorName, DataProcessor.NO_ROW_LIMIT);
    }

    /**
     * <p>
     * Retrieve a list of rows from database with a certain limit range. If the
     * number of returned records is more than the preset limit range, an
     * UnexpectedDataException will be thrown.
     *
     * <p>
     * If DataProcessor.input_key_records_fixed key has value "true" in inputs,
     * absolute fixed number of records is required. An UnexpectedDataException
     * will be thrown if the number of retrieved records is not equal to
     * limitOrFixed.
     *
     * <p>
     * If the limitOrFixed = -1, all records are retrieved.
     *
     * @param inputs            Map of input data
     * @param processorType     A named SQL or direct SQL or stored procedure
     * @param processorName     SQL name or SQL itself or stored procedure name
     * @param limitOrFixed      Number of desired (limit) or fixed records to retrieve
     * @return TableData        The row data
     * @throws com.scooterframework.orm.sqldataexpress.exception.BaseSQLException
     */
    public TableData retrieveRows(Map<String, Object> inputs, String processorType, String processorName, int limitOrFixed)
    throws BaseSQLException {
        return retrieveRows(inputs, processorType, processorName, limitOrFixed, 0);
    }

    /**
     * <p>
     * Retrieve a list of rows from database with a certain limit range. If the
     * number of returned records is more than the preset limit range, an
     * UnexpectedDataException will be thrown.
     *
     * <p>
     * If DataProcessor.input_key_records_fixed key has value "true" in inputs,
     * absolute fixed number of records is required. An UnexpectedDataException
     * will be thrown if the number of retrieved records is not equal to
     * limitOrFixed.
     *
     * <p>
     * If the limitOrFixed = -1, all records are retrieved.
     *
     * offset defaults to 0.
     *
     * @param inputs            Map of input data
     * @param processorType     A named SQL or direct SQL or stored procedure
     * @param processorName     SQL name or SQL itself or stored procedure name
     * @param limitOrFixed      Number of desired (limit) or fixed records to retrieve
     * @param offset            int for offset
     * @return TableData        The row data
     * @throws com.scooterframework.orm.sqldataexpress.exception.BaseSQLException
     */
    public TableData retrieveRows(Map<String, Object> inputs,
                                  String processorType,
                                  String processorName,
                                  int limitOrFixed,
                                  int offset)
    throws BaseSQLException {
        if (processorType == null || processorName == null)
            throw new IllegalArgumentException("processorType or processorName is null.");

        if (inputs == null) inputs = new HashMap<String, Object>();

        inputs.put(DataProcessor.input_key_records_offset, Integer.valueOf(offset));
        inputs.put(DataProcessor.input_key_records_limit, Integer.valueOf(limitOrFixed));

        OmniDTO dto = execute(inputs, processorType, processorName);
        TableData td = dto.getTableData(processorName);

        if (limitOrFixed != DataProcessor.NO_ROW_LIMIT) {
            boolean requireFixed = Util.getBooleanValue(inputs, DataProcessor.input_key_records_fixed, false);
            if (requireFixed) {
                if (td.getTableSize() != 0 && td.getTableSize() != limitOrFixed) {
                    throw new UnexpectedDataException("Failed to retrieveRows for '" +
                      processorName + "': required only " +
                        limitOrFixed + " but retrieved " + td.getTableSize() + ".");
                }
            }
            else {
                if (td.getTableSize() > limitOrFixed) {
                    throw new UnexpectedDataException("Failed to retrieveRows for '" +
                      processorName + "': required limit at most " +
                        limitOrFixed + " but retrieved " + td.getTableSize() + ".");
                }
            }
        }
        return td;
    }


    /**
     * Insert data to database.
     *
     * @param inputs            Map of input data
     * @param processorType     A named SQL or direct SQL or stored procedure
     * @param processorName     SQL name or SQL itself or stored procedure name
     * @throws com.scooterframework.orm.sqldataexpress.exception.BaseSQLException
     */
    public void insert(Map<String, Object> inputs, String processorType, String processorName)
    throws BaseSQLException {
        execute(inputs, processorType, processorName);
    }

    /**
     * Delete data from database.
     *
     * @param inputs            Map of input data
     * @param processorType     A named SQL or direct SQL or stored procedure
     * @param processorName     SQL name or SQL itself or stored procedure name
     * @return int              number of rows deleted
     * @throws com.scooterframework.orm.sqldataexpress.exception.BaseSQLException
     */
    public int delete(Map<String, Object> inputs, String processorType, String processorName)
    throws BaseSQLException {
        OmniDTO dto = execute(inputs, processorType, processorName);
        return dto.getUpdatedRowCount();
    }

    /**
     * Update data in database.
     *
     * @param inputs            Map of input data
     * @param processorType     A named SQL or direct SQL or stored procedure
     * @param processorName     SQL name or SQL itself or stored procedure name
     * @return int              number of rows updated
     * @throws com.scooterframework.orm.sqldataexpress.exception.BaseSQLException
     */
    public int update(Map<String, Object> inputs, String processorType, String processorName)
    throws BaseSQLException {
        OmniDTO dto = execute(inputs, processorType, processorName);
        return dto.getUpdatedRowCount();
    }


    private TransactionManager getTransactionManager() {
        return TransactionManagerUtil.getTransactionManager();
    }

    /**
     * Find or create a new connection
     *
     * @param inputs    Map of inputs
     * @return UserDatabaseConnection object
     */
    private UserDatabaseConnection findOrCreateConnection(Map<String, Object> inputs) {
      UserDatabaseConnection udc = null;
        DatabaseConnectionContext dcc =
            (DatabaseConnectionContext)inputs.get(DataProcessor.input_key_database_connection_context);
        String connectionName = (String)inputs.get(DataProcessor.input_key_database_connection_name);
        if (dcc != null && connectionName != null) {
            throw new IllegalArgumentException("You cannot have both connection name and dcc in the same inputs map.");
        }
        else {
            if (dcc != null) {
                udc = getConnection(dcc);
            }
            else if (connectionName != null) {
                udc = getConnection(connectionName);
            }
            else {
                udc = getConnection();
            }
        }

        return udc;
    }

    /**
     * Find or create a new connection
     *
     * @param ip        InputInfo object
     * @return UserDatabaseConnection object
     */
    private UserDatabaseConnection findOrCreateConnection(InputInfo ip) {
      UserDatabaseConnection udc = null;
        DatabaseConnectionContext dcc = ip.getDatabaseConnectionContext();
        String connectionName = ip.getConnectionName();

        if (dcc != null && connectionName != null) {
            throw new IllegalArgumentException("You cannot have both connection name and dcc in the same InputInfo object.");
        }
        else {
            if (dcc != null) {
                udc = getConnection(dcc);
            }
            else if (connectionName != null) {
                udc = getConnection(connectionName);
            }
            else {
                udc = getConnection();
            }
        }

        return udc;
    }

    /**
     * Get a database connection.
     *
     * A transaction must have been started before this method is called.
     *
     * @return      UserDatabaseConnection     represents the DB connection.
     * @exception   BaseSQLException
     */
    private UserDatabaseConnection getConnection()
    throws BaseSQLException {
      UserDatabaseConnection conn = null;

        try {
            Transaction ts = getTransactionManager().getTransaction();

            if (ts == null || !ts.isTransactionStarted())  {
                throw new TransactionException("getConnection() failed: no started transaction.");
            }

            conn = ts.getConnection();

            if (conn == null) throw new CreateConnectionFailureException("getConnection() failed to create a connection.");
        }
        catch(CreateConnectionFailureException ccfex) {
            throw new BaseSQLException(ccfex);
        }
        catch(Exception ex) {
            throw new BaseSQLException(ex);
        }

        return conn;
    }

    /**
     * Get a database connection based on connection name.
     *
     * A transaction must have been started before this method is called.
     *
     * @return      UserDatabaseConnection     represents the DB connection.
     * @exception   BaseSQLException
     */
    private UserDatabaseConnection getConnection(String connectionName)
    throws BaseSQLException {
      UserDatabaseConnection conn = null;

        try {
            Transaction ts = getTransactionManager().getTransaction();

            if (ts == null || !ts.isTransactionStarted())  {
                throw new TransactionException("getConnection() failed: no started transaction.");
            }

            conn = ts.getConnection(connectionName);

            if (conn == null) throw new CreateConnectionFailureException("getConnection() failed to create a connection.");
        }
        catch(CreateConnectionFailureException ccfex) {
            throw new BaseSQLException(ccfex);
        }
        catch(Exception ex) {
            throw new BaseSQLException(ex);
        }

        return conn;
    }

    /**
     * Get a database connection based on connection context.
     *
     * A transaction must have been started before this method is called.
     *
     * @return      UserDatabaseConnection     represents the DB connection.
     * @exception   BaseSQLException
     */
    private UserDatabaseConnection getConnection(DatabaseConnectionContext dcc)
    throws BaseSQLException {
      UserDatabaseConnection conn = null;

        try {
            Transaction ts = getTransactionManager().getTransaction();

            if (ts == null || !ts.isTransactionStarted())  {
                throw new TransactionException("getConnection() failed: no started transaction.");
            }

            conn = ts.getConnection(dcc);

            if (conn == null) throw new CreateConnectionFailureException("getConnection() failed to create a connection.");
        }
        catch(CreateConnectionFailureException ccfex) {
            throw new BaseSQLException(ccfex);
        }
        catch(Exception ex) {
            throw new BaseSQLException(ex);
        }

        return conn;
    }

    /**
     * execute with output filter
     */
    private OmniDTO executeKeepConnection(UserDatabaseConnection udc,
        Map<String, Object> inputs, String processorType, String processorName, Map<String, String> outputFilters)
    throws BaseSQLException {
        if (udc == null)
            throw new IllegalArgumentException("UserDatabaseConnection object is null.");

        if (processorType == null || processorName == null)
            throw new IllegalArgumentException("processorType or processorName is null.");

        cleanUpInputs(inputs);
        if (inputs == null) inputs = new HashMap<String, Object>();

        OmniDTO returnTO = null;

        try {
            DataProcessor dp = DataProcessorFactory.getInstance().getDataProcessor(udc, processorType, processorName);
            returnTO = dp.execute(udc, convertKeyCase(inputs), outputFilters);
            if (returnTO != null) {
                returnTO.setProcessorType(processorType);
                returnTO.setProcessorName(processorName);
            }
        }
        catch(UnsupportedDataProcessorTypeException udptEx) {
            throw new BaseSQLException("Unsupported DataProcessor Type: " + processorType);
        }
        catch(UnsupportedDataProcessorNameException udpnEx) {
            throw new BaseSQLException("Unsupported DataProcessor Name: " + processorName);
        }
        catch(UnsupportedStoredProcedureAPINameException udpnEx) {
            throw new BaseSQLException("Unsupported DataProcessor Name: " + processorName);
        }

        return returnTO;
    }

    private void cleanUpInputs(Map<String, Object> inputs) {
      if (inputs == null) return;
      //inputs.remove("__sitemesh__filterapplied");
    }

    private Map<String, Object> convertKeyCase(Map<String, Object> map) {
      Map<String, Object> newMap = new HashMap<String, Object>();
      newMap.putAll(map);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            if (key.equals(ActiveRecordConstants.key_finder_sql)) continue;
            newMap.put(key.toUpperCase(), entry.getValue());
        }
        return newMap;
    }

    private String populateChildInputs(int rowIndex, Map<String, Object> childInputs, RowData parent, String childQuery) {
        String newQuery = childQuery;

        Map<String, Object> newInputs = new HashMap<String, Object>();

        for (Map.Entry<String, Object> entry :childInputs.entrySet()) {
            String key = entry.getKey();
            if (key.startsWith("&")) {
                String fkName = key.substring(1);
                String parentKeyName = (String)entry.getValue();
                if (parentKeyName != null) {
                    parentKeyName = parentKeyName.toUpperCase();
                    String keyInQuery = "?" + fkName;
                    if (childQuery.indexOf(keyInQuery) != -1) {
                        Object parentData = parent.getField(parentKeyName);
                        String newKeyName = fkName + "_" + rowIndex;
                        newInputs.put(newKeyName, parentData);
                        newQuery = StringUtil.replace(newQuery, keyInQuery, "?" + newKeyName);
                    }
                }
            }
        }

        childInputs.putAll(newInputs);

        return newQuery;
    }

    //return a string like this: (?fkName1_rowIndex1, ?fkName2_rowIndex2, ...)
    private String populateConditionPart2(int rowIndex, InputInfo childIp,  RowData parent) {
        String part = "(";
        List<String> fkNames = childIp.getFKs();
        if (fkNames != null && fkNames.size() > 0) {
            Map<String, Object> newInputs = new HashMap<String, Object>();
            Map<String, Object> childInputs = childIp.getInputs();
            for (String fkName : fkNames) {
                String parentKeyName = (String)childInputs.get("&"+fkName);
                Object parentData = parent.getField(parentKeyName);
                String newKeyName = fkName + "_" + rowIndex;
                newInputs.put(newKeyName, parentData);
                part = part + "?" + newKeyName + getColumnSqlDataTypeName(parentKeyName, parent) + ",";
            }
            childInputs.putAll(newInputs);

            //remove the last comma
            if (part.endsWith(",")) part = part.substring(0, part.lastIndexOf(','));
            part = part + ")";
        }

        return part;
    }

    private String getColumnSqlDataTypeName(String parentKeyName, RowData parent) {
        String sqlDataTypeName = "";
        RowInfo ri = parent.getRowInfo();
        if (ri != null) {
            sqlDataTypeName = ri.getColmnDataTypeName(ri.getColumnPositionIndex(parentKeyName));
        }

        if (sqlDataTypeName != null && !sqlDataTypeName.equals(""))
            sqlDataTypeName = ":" + sqlDataTypeName;

        return sqlDataTypeName;
    }

    private void linkParentWithChild(TableData parentRt, TableData childRt, String processorName, List<String> connectorList) {
        if(parentRt == null || childRt == null || childRt.getTableSize() == 0) return;

        int size = parentRt.getAllRows().size();
        Map<String, Object> connectorMap = new HashMap<String, Object>();
        for (int i = 0; i < size; i++) {
            RowData parentRow = parentRt.getRow(i);
            populateConnectorMap(connectorMap, parentRow, connectorList);
            parentRow.addChildRowToMap(processorName, getMatchingRowDataList(connectorMap, childRt));

            //clear the map for next row
            connectorMap.clear();
        }
    }

    private void populateConnectorMap(Map<String, Object> connectorMap, RowData parentRow, List<String> connectorList) {
        for (String key : connectorList) {
            connectorMap.put(key, parentRow.getField(key));
        }
    }

    private List<RowData> getMatchingRowDataList(Map<String, Object> connectorMap, TableData childRt) {
        List<RowData> filteredList = new ArrayList<RowData>();
        boolean allPassed = true;
        int size = childRt.getTableSize();
        for (int i=0; i<size; i++) {
            RowData child = childRt.getRow(i);

            for (Map.Entry<String, Object> entry :connectorMap.entrySet()) {
                String key = entry.getKey();
                Object keyData = entry.getValue();
                Object rowData = child.getField(key);
                if (rowData == null || !rowData.toString().equalsIgnoreCase(keyData.toString())) {
                    allPassed = false;
                    break;
                }
            }

            if (allPassed) {
                filteredList.add(child);
            }

            allPassed = true;
        }

        return filteredList;
    }

    private String getNewChildQuery(String query, InputInfo childIp, List<RowData> parentRows) {
        if (query == null || childIp == null ||
            parentRows == null || parentRows.size() == 0)
            return query;//nothing need to be changed.

        //query = SqlUtil.convertToUpperCase(query);
        String childQuery = query;

        //generate a child query as union
        if (InputInfo.CONSTRUCT_CHILD_QUERY_THRU_UNION.equals(childIp.getChildQueryType())) {
            int size = parentRows.size();
            if (size > 0) {
                int rowIndex = 0;
                for (int i = 0; i < size -1; i++) {
                    rowIndex = i;
                    RowData parentRow = parentRows.get(i);
                    childQuery += populateChildInputs(rowIndex, childIp.getInputs(), parentRow, query) + " UNION ";
                }

                //last row
                rowIndex = size -1;
                RowData parentRow = (RowData)parentRows.get(rowIndex);
                childQuery += populateChildInputs(rowIndex, childIp.getInputs(), parentRow, query);
            }
        }
        else
        if (InputInfo.CONSTRUCT_CHILD_QUERY_MAKE_NEW_WHERE_CLAUSE.equals(childIp.getChildQueryType())) {
            int size = parentRows.size();
            if (size > 0) {
                String conditionPart1 = "(" + childIp.getFKString() + ") in ";
                String conditionPart2 = "";
                int rowIndex = 0;
                for (int i = 0; i < size -1; i++) {
                    rowIndex = i;
                    RowData parentRow = parentRows.get(i);
                    conditionPart2 += populateConditionPart2(rowIndex, childIp, parentRow) + ", ";
                }

                //last row
                rowIndex = size -1;
                RowData parentRow = parentRows.get(rowIndex);
                conditionPart2 += populateConditionPart2(rowIndex, childIp, parentRow);
                childQuery += " WHERE " + conditionPart1 + "(" + conditionPart2 + ")";
            }
        }
        else
        if (InputInfo.CONSTRUCT_CHILD_QUERY_ADD_TO_WHERE_CLAUSE.equals(childIp.getChildQueryType())) {
            int size = parentRows.size();
            if (size > 0) {
                String conditionPart1 = "(" + childIp.getFKString() + ") in ";
                String conditionPart2 = "";
                int rowIndex = 0;
                for (int i = 0; i < size -1; i++) {
                    rowIndex = i;
                    RowData parentRow = (RowData)parentRows.get(i);
                    conditionPart2 += populateConditionPart2(rowIndex, childIp, parentRow) + ", ";
                }

                //last row
                rowIndex = size -1;
                RowData parentRow = (RowData)parentRows.get(rowIndex);
                conditionPart2 += populateConditionPart2(rowIndex, childIp, parentRow);
                childQuery += " AND " + conditionPart1 + "(" + conditionPart2 + ")";
            }
        }

        return childQuery;
    }

    private LogUtil log = LogUtil.getLogger(this.getClass().getName());
}
TOP

Related Classes of com.scooterframework.orm.sqldataexpress.service.SqlServiceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.