.Node;

/**
 * The SQL execution of select by condition-bean. <br>
 * The first element of arguments should be condition-bean (and not null).
 * @author jflute
 */
public class SelectCBExecution extends AbstractFixedArgExecution {

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    protected final TnResultSetHandler _resultSetHandler;

    // ===================================================================================
    //                                                                         Constructor
    //                                                                         ===========
    /**
     * Constructor.
     * @param dataSource The data source for a database connection. (NotNull)
     * @param statementFactory The factory of statement. (NotNull)
     * @param argNameTypeMap The map of names and types for arguments. (NotNull)
     * @param resultSetHandler The handler of result set. (NotNull)
     */
    public SelectCBExecution(DataSource dataSource, StatementFactory statementFactory, Map<String, Class<?>> argNameTypeMap,
            TnResultSetHandler resultSetHandler) {
        super(dataSource, statementFactory, argNameTypeMap);
        assertObjectNotNull("resultSetHandler", resultSetHandler);
        _resultSetHandler = resultSetHandler;
    }

    // ===================================================================================
    //                                                                            Resource
    //                                                                            ========
    @Override
    public Object execute(Object[] args) {
        final ConditionBean cb = extractConditionBean(args);
        final Object splitResult = processPagingSelectAndQuerySplit(args, cb);
        if (splitResult!= null) { // rarely
            return splitResult;
        }
        return superExecute(args); // basically here
    }

    protected Object superExecute(Object[] args) {
        return super.execute(args);
    }

    @Override
    protected Node getRootNode(Object[] args) {
        return analyzeTwoWaySql(extractTwoWaySql(args)); // dynamic analysis
    }

    protected String extractTwoWaySql(Object[] args) {
        final ConditionBean cb = extractConditionBean(args);
        return cb.getSqlClause().getClause();
    }

    // -----------------------------------------------------
    //                                     Argument Handling
    //                                     -----------------
    protected ConditionBean extractConditionBean(Object[] args) {
        assertArgsValid(args);
        final Object firstElement = args[0];
        assertObjectNotNull("args[0]", firstElement);
        assertFirstElementConditionBean(firstElement);
        final ConditionBean cb = (ConditionBean) firstElement;
        return cb;
    }

    protected void assertArgsValid(Object[] args) {
        if (args == null) {
            String msg = "The argument 'args' should not be null.";
            throw new IllegalArgumentException(msg);
        }
        if (args.length == 0) {
            String msg = "The argument 'args' should not be empty.";
            throw new IllegalArgumentException(msg);
        }
    }

    protected void assertFirstElementConditionBean(Object firstElement) {
        if (!(firstElement instanceof ConditionBean)) {
            String msg = "The first element of 'args' should be condition-bean: " + firstElement.getClass();
            throw new IllegalArgumentException(msg);
        }
    }

    // ===================================================================================
    //                                                       Paging Select and Query Split
    //                                                       =============================
    protected Object processPagingSelectAndQuerySplit(Object[] args, ConditionBean cb) {
        if (!cb.canPagingSelectAndQuerySplit()) {
            return null;
        }
        if (!cb.isFetchScopeEffective()) {
            return null;
        }
        final DBMeta dbmeta = cb.asDBMeta();
        final PrimaryInfo primaryInfo = dbmeta.getPrimaryInfo();
        if (primaryInfo.isCompoundKey()) { // basically no way, already checked
            return null;
        }
        final ColumnInfo pkColumn = primaryInfo.getFirstColumn();
        final SqlClause sqlClause = cb.getSqlClause();
        final List<Object> pkList = doSplitSelectFirst(args, cb, dbmeta, sqlClause);
        if (pkList == null) { // no way just in case
            return null;
        }
        if (pkList.isEmpty()) {
            return pkList;
        }
        return doSplitSelectSecond(args, cb, pkColumn, sqlClause, pkList);
    }

    protected List<Object> doSplitSelectFirst(Object[] args, ConditionBean cb, DBMeta dbmeta, SqlClause sqlClause) {
        final List<Object> pkList = new ArrayList<Object>();
        try {
            sqlClause.enablePKOnlySelectForcedly();
            final Object firstResult = superExecute(args);
            if (firstResult == null ||!(firstResult instanceof List)) { // no way just in case
                return null;
            }
            @SuppressWarnings("unchecked")
            final List<Entity> entityList = (List<Entity>) firstResult;
            for (Entity entity : entityList) {
                final Map<String, Object> primaryKeyMap = dbmeta.extractPrimaryKeyMap(entity);
                pkList.add(primaryKeyMap.values().iterator().next()); // only-one here
            }
            return pkList;
        } finally {
            sqlClause.disablePKOnlySelectForcedly();
        }
    }

    protected Object doSplitSelectSecond(Object[] args, ConditionBean cb, ColumnInfo pkColumn, SqlClause sqlClause, List<Object> pkList) {
        final int fetchSize = sqlClause.getFetchSize();
        final int fetchPageNumber = sqlClause.getFetchPageNumber();
        try {
            sqlClause.backupWhereClauseOnBaseQuery();
            sqlClause.clearWhereClauseOnBaseQuery();
            sqlClause.suppressFetchScope();

            // order by is inherited
            // basically small list here so one more order-by is not problem
            final String ckey = ConditionKey.CK_IN_SCOPE.getConditionKey();
            cb.localCQ().invokeQuery(pkColumn.getColumnDbName(), ckey, pkList);
            return superExecute(args);
        } finally {
            sqlClause.restoreWhereClauseOnBaseQuery();
            sqlClause.fetchFirst(fetchSize);
            sqlClause.fetchPage(fetchPageNumber);
        }
    }

    // ===================================================================================
    //                                                                             Handler
    //                                                                             =======
    @Override
    protected TnBasicParameterHandler newBasicParameterHandler(String executedSql) {
        return new TnBasicSelectHandler(_dataSource, executedSql, _resultSetHandler, _statementFactory);
    }

    // ===================================================================================
    //                                                                              Filter
    //                                                                              ======
    @Override
    protected String filterExecutedSql(CommandContext ctx) {
        final String filteredSql = doFilterExecutedSqlByCallbackFilter(super.filterExecutedSql(ctx));
        saveDisplaySqlLogResource(ctx, filteredSql, ctx.getBindVariables());
        return filteredSql;
    }

    protected void saveDisplaySqlLogResource(CommandContext ctx, String filteredSql, Object[] args) {
        // to relation optional thrower's message, and to be serializable
        InternalMapContext.setDisplaySqlLogResourceSql(filteredSql);
        InternalMapContext.setDisplaySqlLogResourceParams(args);
    }

    protected String doFilterExecutedSqlByCallbackFilter(String executedSql) {
        final SqlStringFilter sqlStringFilter = getSqlStringFilter();
        if (sqlStringFilter!= null) {
            final BehaviorCommandMeta meta = ResourceContext.behaviorCommand();
            final String filteredSql = sqlStringFilter.filterSelectCB(meta, executedSql);
            return filteredSql!= null? filteredSql : executedSql;
        }
        return executedSql;
    }

    protected SqlStringFilter getSqlStringFilter() {
        if (!CallbackContext.isExistSqlStringFilterOnThread()) {
            return null;
        }
        return CallbackContext.getCallbackContextOnThread().getSqlStringFilter();
    }

    // ===================================================================================
    //                                                                        SQL Handling
    //                                                                        ============
    @Override
    protected boolean isBlockNullParameter() {
        return true; // because the SQL is select
    }
}
Abstract: With the recent increase in the traffic, internet service providers are forced to seek solutions for better and improved services within the available resources. While the current traffic loads have to be taken into account for computation of paths in routing protocols, network applications require the shortest paths to be used for a speedy communication. The open shortest path first (OSPF) is the routing algorithm used to find the shortest path from source to destination, however, the overloading of the