Package org.apache.harmony.sql.internal.rowset

Source Code of org.apache.harmony.sql.internal.rowset.JoinRowSetImpl$CachedRowComparator

/*
* 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.
*/
package org.apache.harmony.sql.internal.rowset;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.sql.RowSet;
import javax.sql.RowSetMetaData;
import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.JdbcRowSet;
import javax.sql.rowset.JoinRowSet;
import javax.sql.rowset.Joinable;
import javax.sql.rowset.RowSetMetaDataImpl;
import javax.sql.rowset.spi.SyncFactoryException;
import javax.sql.rowset.spi.SyncProviderException;

import org.apache.harmony.sql.internal.nls.Messages;

public class JoinRowSetImpl extends WebRowSetImpl implements JoinRowSet {

    private List<RowSet> rsList;

    private CachedRowSet copyFirstRs;

    private List<Integer> matchColIndexs;

    private List<String> matchColNames;

    private int joinType;

    private static String MERGED_COLUMN_NAME = "MergedCol"; //$NON-NLS-1$

    // Whether the rows can be sorted using object in matched index.
    private boolean isSortable;

    public JoinRowSetImpl() throws SyncFactoryException {
        super();
        initProperties();
    }

    @Override
    public void acceptChanges() throws SyncProviderException {
        acceptChanges(conn);
    }

    @Override
    protected boolean doAbsolute(int row, boolean checkType)
            throws SQLException {
        return super.doAbsolute(row, false);
    }

    private void initProperties() {
        rsList = new ArrayList<RowSet>();
        matchColIndexs = new ArrayList<Integer>();
        matchColNames = new ArrayList<String>();
        joinType = INNER_JOIN;
        setIsNotifyListener(false);
        rows = new ArrayList<CachedRow>();
    }

    private void composeMetaData(ResultSetMetaData rsmd, int matchColumn)
            throws SQLException {
        if (getMetaData() == null) {
            if (rsmd instanceof RowSetMetaData) {
                setMetaData((RowSetMetaData) rsmd);
            } else {
                setMetaData(copyMetaData(rsmd));
            }
        } else {
            int colCount = getMetaData().getColumnCount()
                    + rsmd.getColumnCount() - 1;
            RowSetMetaData rowSetMetaData = new RowSetMetaDataImpl();
            rowSetMetaData.setColumnCount(colCount);
            for (int i = 1; i <= getMetaData().getColumnCount(); i++) {
                doCopyMetaData(rowSetMetaData, i, getMetaData(), i);
                if (i == matchColIndexs.get(0).intValue()) {
                    rowSetMetaData.setColumnName(i, MERGED_COLUMN_NAME);
                }
            }
            int index = 0;
            for (int j = 1; j <= rsmd.getColumnCount(); j++) {
                if (j == matchColumn) {
                    continue;
                }
                index++;
                doCopyMetaData(rowSetMetaData, getMetaData().getColumnCount()
                        + index, rsmd, j);
            }
            setMetaData(rowSetMetaData);
        }
    }

    public void addRowSet(Joinable rowset) throws SQLException {
        if (rowset == null || !(rowset instanceof RowSet)) {
            // rowset.33=Not a rowset
            throw new SQLException(Messages.getString("rowset.33")); //$NON-NLS-1$
        }

        RowSet currentRs = (RowSet) rowset;
        if (currentRs.getMetaData() == null) {
            // rowset.32=The given rowset is empty
            throw new SQLException(Messages.getString("rowset.32")); //$NON-NLS-1$
        }

        int matchCol = -1;
        try {
            if (rowset.getMatchColumnIndexes() != null
                    && rowset.getMatchColumnIndexes().length > 0) {
                matchCol = rowset.getMatchColumnIndexes()[0];
                if (matchCol <= 0
                        || matchCol > currentRs.getMetaData().getColumnCount()) {
                    matchCol = -2;
                }
            }
        } catch (SQLException e) {
            try {
                if (rowset.getMatchColumnNames() != null
                        && rowset.getMatchColumnNames().length > 0) {
                    try {
                        matchCol = currentRs.findColumn(rowset
                                .getMatchColumnNames()[0]);
                    } catch (SQLException e1) {
                        matchCol = -3;
                    }
                }
            } catch (SQLException e2) {
                // ignore
            }
        } finally {
            if (matchCol == -1) {
                // rowset.34=Not set a match column
                throw new SQLException(Messages.getString("rowset.34")); //$NON-NLS-1$
            } else if (matchCol == -2) {
                // rowset.35=Not a valid match olumn index
                throw new SQLException(Messages.getString("rowset.35")); //$NON-NLS-1$
            } else if (matchCol == -3) {
                // rowset.1=Not a valid column name
                throw new SQLException(Messages.getString("rowset.1")); //$NON-NLS-1$
            }
        }
        addRowSet(currentRs, matchCol);
    }

    public void addRowSet(RowSet rowset, int columnIdx) throws SQLException {
        if (rowset == null) {
            throw new NullPointerException();
        }
        if (rowset.getMetaData() == null) {
            // rowset.32=The given rowset is empty
            throw new SQLException(Messages.getString("rowset.32")); //$NON-NLS-1$
        }
        if (columnIdx <= 0 || columnIdx > rowset.getMetaData().getColumnCount()) {
            // rowset.35=Not a valid match olumn index
            throw new SQLException(Messages.getString("rowset.35")); //$NON-NLS-1$
        }

        int type = rowset.getMetaData().getColumnType(columnIdx);
        if (getMetaData() != null
                && getMetaData().getColumnType(getMatchColumnIndexes()[0]) != type) {
            setMetaData(null);
            rows = null;
            // rowset.10=Data Type Mismatch
            throw new SQLException(Messages.getString("rowset.10")); //$NON-NLS-1$
        }

        if (getMetaData() == null
                && (type == Types.BINARY || type == Types.LONGVARBINARY
                        || type == Types.VARBINARY || type == Types.BLOB
                        || type == Types.CLOB || type == Types.ARRAY
                        || type == Types.LONGVARCHAR
                        || type == Types.JAVA_OBJECT || type == Types.OTHER || type == Types.NULL)) {
            // rowset.10=Data Type Mismatch
            throw new SQLException(Messages.getString("rowset.10")); //$NON-NLS-1$
        }

        composeMetaData(rowset.getMetaData(), columnIdx);

        columnCount = getMetaData().getColumnCount();
        // Reassign columnTypes.
        columnTypes = new Class[columnCount];
        for (int i = 1; i <= columnTypes.length; ++i) {
            columnTypes[i - 1] = TYPE_MAPPING.get(Integer.valueOf(meta
                    .getColumnType(i)));
        }

        matchColIndexs.add(Integer.valueOf(columnIdx));

        if (rowset instanceof JdbcRowSet) {
            CachedRowSet convertRowset = new CachedRowSetImpl();
            convertRowset.populate(rowset);
            rsList.add(convertRowset);
        } else {
            rsList.add(rowset);
        }

        if (rsList.size() == 1) {
            initSortable();
        }
        try {
            innerJoin();
        } catch (CloneNotSupportedException e) {
            SQLException sqlException = new SQLException();
            sqlException.initCause(e);
            throw sqlException;
        }

        if (rsList.size() == 1) {
            copyFirstRs = ((CachedRowSet) rsList.get(0)).createCopy();
            setMatchColumn(columnIdx);
        }
    }

    public void addRowSet(RowSet rowset, String columnName) throws SQLException {
        if (rowset == null) {
            throw new NullPointerException();
        }
        if (rowset.getMetaData() == null) {
            // rowset.32=The given rowset is empty
            throw new SQLException(Messages.getString("rowset.32")); //$NON-NLS-1$
        }

        int columnIdx = -1;
        try {
            columnIdx = rowset.findColumn(columnName);
        } catch (SQLException e) {
            throw e;
        }
        addRowSet(rowset, columnIdx);
    }

    public void addRowSet(RowSet[] rowset, int[] columnIdx) throws SQLException {
        if (rowset == null || columnIdx == null || rowset.length == 0
                || columnIdx.length == 0) {
            throw new NullPointerException();
        }
        if (rowset.length != columnIdx.length) {
            // rowset.36=Number of elements of two arrays don't equal
            throw new SQLException(Messages.getString("rowset.36")); //$NON-NLS-1$
        }
        for (int i = 0; i < rowset.length; i++) {
            addRowSet(rowset[i], columnIdx[i]);
        }
    }

    public void addRowSet(RowSet[] rowset, String[] columnName)
            throws SQLException {
        if (rowset == null || columnName == null || rowset.length == 0
                || columnName.length == 0) {
            throw new NullPointerException();
        }
        if (rowset.length != columnName.length) {
            // rowset.36=Number of elements of two arrays don't equal
            throw new SQLException(Messages.getString("rowset.36")); //$NON-NLS-1$
        }
        for (int i = 0; i < rowset.length; i++) {
            addRowSet(rowset[i], columnName[i]);
        }
    }

    public int getJoinType() throws SQLException {
        return joinType;
    }

    public String[] getRowSetNames() throws SQLException {
        if (rsList == null || rsList.size() == 0) {
            return new String[0];
        }
        String[] rowsetNames = new String[rsList.size()];
        for (int i = 0; i < rsList.size(); i++) {
            if (rsList.get(i) instanceof CachedRowSet) {
                CachedRowSet cachedRs = (CachedRowSet) rsList.get(i);
                if (cachedRs.getTableName() == null) {
                    // rowset.37=The RowSet doesn't set the table name
                    throw new SQLException(Messages.getString("rowset.37")); //$NON-NLS-1$
                }
                rowsetNames[i] = cachedRs.getTableName();
            } else {
                // rowset.37=The RowSet doesn't set the table name
                throw new SQLException(Messages.getString("rowset.37")); //$NON-NLS-1$
            }
        }
        return rowsetNames;
    }

    public Collection<?> getRowSets() throws SQLException {
        return rsList;
    }

    /**
     * Gets a sql clause which specify the join action.
     */
    public String getWhereClause() throws SQLException {
        int size = rsList.size();

        // If 0 rowSets in it, return "".
        if (size == 0) {
            return ""; //$NON-NLS-1$
        }

        String whereClause;
        String tableName;
        int metaColumnCount;
        int matchIndex;
        if (size == 1) {
            tableName = ((CachedRowSet) rsList.get(0)).getTableName();
            if (tableName == null) {
                throw new SQLException(Messages.getString("rowset.39")); //$NON-NLS-1$
            }
            metaColumnCount = meta.getColumnCount();
            whereClause = "Select"; //$NON-NLS-1$
            for (int i = 1; i <= metaColumnCount; i++) {
                whereClause += " " + tableName + "." + meta.getColumnName(i); //$NON-NLS-1$ //$NON-NLS-2$
                if (i < metaColumnCount) {
                    whereClause += ","; //$NON-NLS-1$
                } else {
                    whereClause += " from " + tableName; //$NON-NLS-1$
                }
            }
        } else {
            whereClause = "Select"; //$NON-NLS-1$
            CachedRowSet rowSet;
            String matchName;

            for (int i = 0; i < size; i++) {
                rowSet = (CachedRowSet) rsList.get(i);
                tableName = rowSet.getTableName();
                if (tableName == null) {
                    throw new SQLException(Messages.getString("rowset.39")); //$NON-NLS-1$
                }

                matchIndex = matchColIndexs.get(0).intValue();
                if (i == 0) {
                    matchName = rowSet.getMetaData().getColumnName(matchIndex);
                    whereClause += " " + tableName + "." + matchName + ","; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                }

                metaColumnCount = rowSet.getMetaData().getColumnCount();
                for (int j = 1; j < matchIndex; j++) {
                    matchName = rowSet.getMetaData().getColumnName(j);
                    whereClause += " " + tableName + "." + matchName; //$NON-NLS-1$ //$NON-NLS-2$

                    if (j != metaColumnCount - 1 || i != size - 1) {
                        whereClause += ","; //$NON-NLS-1$
                    } else {
                        whereClause += " "; //$NON-NLS-1$
                    }
                }

                for (int j = matchIndex + 1; j <= metaColumnCount; j++) {
                    matchName = rowSet.getMetaData().getColumnName(j);
                    whereClause += " " + tableName + "." + matchName; //$NON-NLS-1$ //$NON-NLS-2$

                    if (j != metaColumnCount || i != size - 1) {
                        whereClause += ","; //$NON-NLS-1$
                    } else {
                        whereClause += " "; //$NON-NLS-1$
                    }
                }
            }
            whereClause += "from "; //$NON-NLS-1$
            for (int i = 0; i < size; i++) {
                rowSet = (CachedRowSet) rsList.get(i);
                tableName = rowSet.getTableName();

                whereClause += tableName;
                if (i != size - 1) {
                    whereClause += ", "; //$NON-NLS-1$
                } else {
                    whereClause += " "; //$NON-NLS-1$
                }
            }

            whereClause += "where "; //$NON-NLS-1$
            CachedRowSet firstRowSet = (CachedRowSet) rsList.get(0);
            String firstTableName = firstRowSet.getTableName();
            String firstMatchName = firstRowSet.getMetaData().getColumnName(
                    matchColIndexs.get(0).intValue());
            for (int i = 1; i < size; i++) {
                rowSet = (CachedRowSet) rsList.get(i);
                tableName = rowSet.getTableName();
                matchIndex = matchColIndexs.get(i).intValue();
                matchName = rowSet.getMetaData().getColumnName(matchIndex);

                whereClause += firstTableName + "." + firstMatchName + " = "; //$NON-NLS-1$ //$NON-NLS-2$
                whereClause += tableName + "." + matchName; //$NON-NLS-1$
                if (i != size - 1) {
                    whereClause += " and "; //$NON-NLS-1$
                }
            }
        }

        return whereClause;
    }

    public void setJoinType(int joinType) throws SQLException {
        if (supportsJoinType(joinType)) {
            this.joinType = joinType;
        } else {
            // rowset.38=This type of join is not supported
            throw new SQLException(Messages.getString("rowset.38")); //$NON-NLS-1$
        }
    }

    public boolean supportsCrossJoin() {
        return supportsJoinType(CROSS_JOIN);
    }

    public boolean supportsFullJoin() {
        return supportsJoinType(FULL_JOIN);
    }

    public boolean supportsInnerJoin() {
        return supportsJoinType(INNER_JOIN);
    }

    public boolean supportsLeftOuterJoin() {
        return supportsJoinType(LEFT_OUTER_JOIN);
    }

    public boolean supportsRightOuterJoin() {
        return supportsJoinType(RIGHT_OUTER_JOIN);
    }

    private boolean supportsJoinType(int type) {
        if (type == INNER_JOIN) {
            return true;
        }
        return false;
    }

    public CachedRowSet toCachedRowSet() throws SQLException {
        if (rsList.size() == 0) {
            CachedRowSetImpl toCrset = new CachedRowSetImpl();
            toCrset.setRows(new ArrayList<CachedRow>(), 0);
            return toCrset;
        } else if (rsList.size() == 1) {
            CachedRowSet toCrset = ((CachedRowSet) rsList.get(0)).createCopy();
            toCrset.setMetaData(meta);
            return toCrset;
        } else {
            CachedRowSetImpl toCrset = new CachedRowSetImpl();
            toCrset.setRows(rows, meta.getColumnCount());
            toCrset.setMetaData(meta);
            toCrset.columnTypes = columnTypes;
            toCrset.setTypeMap(rsList.get(0).getTypeMap());
            if (rsList.get(0).getUrl() != null) {
                toCrset.setUrl(rsList.get(0).getUrl());
                toCrset.setUsername(rsList.get(0).getUsername());
                toCrset.setPassword(rsList.get(0).getPassword());
            } else if (rsList.get(0).getDataSourceName() != null) {
                toCrset.setDataSourceName(rsList.get(0).getDataSourceName());
            }
            return toCrset;
        }
    }

    @Override
    public void populate(ResultSet rs) throws SQLException {
        // do nothing
    }

    @Override
    public void populate(ResultSet rs, int startRow) throws SQLException {
        // do nothing
    }

    @Override
    public void execute() throws SQLException {
        if (rsList.size() == 0) {
            throw new SQLException();
        } else if (rsList.size() == 1) {
            try {
                copyFirstRs.execute();
                super.populate(copyFirstRs);
            } catch (SQLException e) {
                setRows(new ArrayList<CachedRow>(), getMetaData()
                        .getColumnCount());
                throw e;
            }
        } else {
            setRows(new ArrayList<CachedRow>(), getMetaData().getColumnCount());
            throw new SQLException();
        }
    }

    @Override
    public void execute(Connection connection) throws SQLException {
        if (rsList.size() == 0) {
            throw new SQLException();
        } else if (rsList.size() == 1) {
            try {
                copyFirstRs.execute(connection);
                super.populate(copyFirstRs);
            } catch (SQLException e) {
                setRows(new ArrayList<CachedRow>(), getMetaData()
                        .getColumnCount());
                throw e;
            }
        } else {
            setRows(new ArrayList<CachedRow>(), getMetaData().getColumnCount());
            throw new SQLException();
        }
    }

    /**
     * Join the data with another CachedRowSet. It updates rows,
     * currentRowIndex, currentRow and columnCount. It doesn't update its
     * RowSetMetaData or any other status.
     *
     * @throws SQLException
     * @throws CloneNotSupportedException
     */
    private void innerJoin() throws SQLException, CloneNotSupportedException {
        CachedRowSetImpl rowSetToAdd = (CachedRowSetImpl) rsList.get(rsList
                .size() - 1);

        // If it is the first RowSet added, then just copy the data of
        // rowSetToAdd.
        // Since here the RowSet has been added to rsList, so it is 2 to
        // compared with, not 1.
        if (rsList.size() < 2) {
            // Create a new arrayList to store the copied data.
            // Since the size will be the same with the rowSetToAdd, so
            // initialing the list with this size will improve performance.
            ArrayList<CachedRow> newRows = new ArrayList<CachedRow>(
                    rowSetToAdd.rows.size());

            // Make clones for all the CachedRows in rowSetToAdd, regardless of
            // whether it is deleted.
            CachedRow row;
            for (int i = 0; i < rowSetToAdd.rows.size(); i++) {
                row = rowSetToAdd.rows.get(i);
                if (!row.isDelete() || rowSetToAdd.getShowDeleted()) {
                    newRows.add(row.createClone());
                }
            }

            // Sets the rows and columnCount.
            setRows(newRows, rowSetToAdd.columnCount);

            // Set the currentRow and currentRowIndex.
            currentRowIndex = rowSetToAdd.currentRowIndex;
            if (currentRowIndex > 0 && currentRowIndex <= rows.size()) {
                currentRow = rows.get(currentRowIndex - 1);
            } else {
                currentRow = null;
            }

            // Inits other properties.
            originalResultSet = rowSetToAdd.originalResultSet;
            if (rowSetToAdd.conn != null) {
                conn = rowSetToAdd.conn;
            }
            setTypeMap(rowSetToAdd.getTypeMap());
            this.setTableName(rowSetToAdd.getTableName());
            setShowDeleted(rowSetToAdd.getShowDeleted());
        } else {
            // Get the match index of itself and the rowSet to added.
            int matchIndex = matchColIndexs.get(0).intValue();
            // Here we can sure rsList.size() > 1
            int matchIndexOfToAdd = matchColIndexs.get(rsList.size() - 1)
                    .intValue();

            // The comparator used to sort the rows of itself (When it can be
            // sorted), and to compare
            // the rows between these two rowSets.
            CachedRowComparator comparator;

            // If the rows can be sorted on the object of match index, call
            // sortJoinRows.
            // Otherwise call iterativeJoinRows.
            if (isSortable()) {
                comparator = new CachedRowComparator(matchIndex,
                        matchIndexOfToAdd, true);
                sortJoinRows(rowSetToAdd, matchIndex, matchIndexOfToAdd,
                        comparator);
            } else {
                comparator = new CachedRowComparator(matchIndex,
                        matchIndexOfToAdd, false);
                iterativeJoinRows(rowSetToAdd, matchIndex, matchIndexOfToAdd,
                        comparator);
            }

            // Set the cursor of rowSetToAdd to the last.
            rowSetToAdd.last();

            // Set the cursor of itself to beforeFirst.
            beforeFirst();
        }
    }

    private boolean isSortable() {
        return isSortable;
    }

    private void initSortable() {
        Class<?> clazz = columnTypes[matchColIndexs.get(0).intValue() - 1];
        Class[] classes = clazz.getInterfaces();
        isSortable = false;
        for (Class<?> c : classes) {
            if (c.equals(Comparable.class)) {
                isSortable = true;
                break;
            }
        }
    }

    private void iterativeJoinRows(CachedRowSetImpl rowSetToAdd,
            int matchColumnIndex, int matchColumnIndexOfToAdd,
            CachedRowComparator comparator) throws SQLException {
        // The row from itself.
        CachedRow row;
        // The row from rowSet to add.
        CachedRow rowToAdd;
        // The row will be created to join the row and rowToAdd.
        CachedRow newRow;

        /*
         * Create a new arrayList to store the copied data. Since the size will
         * surely less then the min of the two rowSets, so initialing the list
         * with half of the min will improve performance.
         */
        ArrayList<CachedRow> newRows = new ArrayList<CachedRow>(Math.min(rows
                .size(), rowSetToAdd.rows.size()) / 2);

        /*
         * Computes the column count of rowSetToAdd, the result rowSet, the
         * orignal rowSet.
         */
        int addedColumnCount = rowSetToAdd.getMetaData().getColumnCount();
        int resultColumnCount = this.getMetaData().getColumnCount();

        /*
         * Since only one matched index is supported, and these two matched
         * columns will merge to one column, so the original column count is
         * just the minus of result comlumn count and added column count plus 1.
         */
        int originalColumnCount = resultColumnCount + 1 - addedColumnCount;

        /*
         * Iterates two rowsets, compare rowNum1 * rowNum2 times. If match,
         * construct a new row, add it to the row list.
         */
        this.beforeFirst();
        while (this.next()) {
            row = this.getCurrentRow();
            // If the value is null, just jump to next row.
            // Since null won't match anything, even null.
            if (row.getObject(matchColumnIndex) != null) {
                rowSetToAdd.beforeFirst();
                while (rowSetToAdd.next()) {
                    rowToAdd = rowSetToAdd.getCurrentRow();
                    if (comparator.compare(row, rowToAdd) == 0) {
                        // It match, contruct a new row, add it to list.
                        newRow = constructNewRow(row, rowToAdd,
                                matchColumnIndex, matchColumnIndexOfToAdd,
                                resultColumnCount, originalColumnCount);
                        newRows.add(newRow);
                    }
                }
            }
        }

        // Sets the rows and column count.
        setRows(newRows, resultColumnCount);
    }

    private void sortJoinRows(CachedRowSetImpl rowSetToAdd,
            int matchColumnIndex, int matchColumnIndexOfToAdd,
            CachedRowComparator comparator) throws SQLException {
        // The row from itself.
        CachedRow row;
        // The row from rowSet to add.
        CachedRow rowToAdd;
        // The row will be created to join the row and rowToAdd.
        CachedRow newRow;

        /*
         * Create a new arrayList to store the copied data. Since the size will
         * surely less then the min of the two rowSets, so initialing the list
         * with half of the min will improve performance.
         */
        ArrayList<CachedRow> newRows = new ArrayList<CachedRow>(Math.min(rows
                .size(), rowSetToAdd.rows.size()) / 2);

        /*
         * Computes the column count of rowSetToAdd, the result rowSet, the
         * orignal rowSet.
         */
        int addedColumnCount = rowSetToAdd.getMetaData().getColumnCount();
        int resultColumnCount = this.getMetaData().getColumnCount();

        /*
         * Since only one matched index is supported, and these two matched
         * columns will merge to one column, so the original column count is
         * just the minus of result comlumn count and added column count plus 1.
         */
        int originalColumnCount = resultColumnCount + 1 - addedColumnCount;

        /*
         * Sort the original rows. Set both the column to compared to the match
         * index of original rows, since the comprasion will happened inside the
         * orignal rows.
         */
        comparator.setFirstIndex(matchColumnIndex);
        comparator.setSecondIndex(matchColumnIndex);
        Collections.sort(rows, comparator);

        /*
         * Then comparator will be used to compare the object from two rowSets,
         * so set firstIndex of comparator to match index of itself, and second
         * index to match index of rowSet to add.
         */
        comparator.setFirstIndex(matchColumnIndex);
        comparator.setSecondIndex(matchColumnIndexOfToAdd);

        int position;
        /*
         * Iterates the rows of rowSetToAdd, find matched row in orignal rows
         * using binary search.(It has been sorted).
         */
        rowSetToAdd.beforeFirst();
        while (rowSetToAdd.next()) {
            rowToAdd = rowSetToAdd.getCurrentRow();

            /*
             * If the value is null, just jump to next row. Since null won't
             * match anything, even null.
             */
            if (rowToAdd.getObject(matchColumnIndexOfToAdd) == null) {
                continue;
            }

            // Find the position of the matched row in original rows.
            position = Collections.binarySearch(rows, rowToAdd, comparator);

            // Not found, jump to next row.
            if (position < 0) {
                continue;
            }

            row = rows.get(position);
            /*
             * If row is deleted and showDeleted is false, it will not be
             * joined.
             */
            if (getShowDeleted() || !row.isDelete()) {
                // Contruct a new row, add it to list.
                newRow = constructNewRow(row, rowToAdd, matchColumnIndex,
                        matchColumnIndexOfToAdd, resultColumnCount,
                        originalColumnCount);
                newRows.add(newRow);

                /*
                 * Since there may be other rows also match, so we have to
                 * examine the before positions and after positions until they
                 * don't match. Remember, Collections.binarySearch does NOT
                 * guarantee which row will be found if multiple element are
                 * equal to the search value.
                 */
                for (int i = position - 1; i >= 0; i--) {
                    row = rows.get(i);
                    if (comparator.compare(row, rowToAdd) == 0) {
                        newRow = constructNewRow(row, rowToAdd,
                                matchColumnIndex, matchColumnIndexOfToAdd,
                                resultColumnCount, originalColumnCount);
                        newRows.add(newRow);
                    } else {
                        break;
                    }
                }

                for (int i = position + 1; i < rows.size(); i++) {
                    row = rows.get(i);
                    if (comparator.compare(row, rowToAdd) == 0) {
                        newRow = constructNewRow(row, rowToAdd,
                                matchColumnIndex, matchColumnIndexOfToAdd,
                                resultColumnCount, originalColumnCount);
                        newRows.add(newRow);
                    } else {
                        break;
                    }
                }
            }

        }

        // Sets the rows and column count.
        setRows(newRows, resultColumnCount);
    }

    /**
     * Construct a new CachedRow which will contain the data from both rows,
     * excluding matched column, which will only appear once in the result row..
     *
     * @param row
     *            The first row to join.
     * @param rowToAdd
     *            The second row to join.
     * @param matchColumnIndex
     *            The match index of first row.
     * @param matchColumnIndexOfToAdd
     *            The match index of second row.
     * @param resultColumnCount
     *            The column count of the result row.
     * @param originalColumnCount
     *            The column count of original row.
     * @return The new created CachedRow.
     */
    private CachedRow constructNewRow(CachedRow row, CachedRow rowToAdd,
            int matchColumnIndex, int matchColumnIndexOfToAdd,
            int resultColumnCount, int originalColumnCount) {
        Object[] rowData;

        rowData = new Object[resultColumnCount];

        int i = 0;
        for (; i < matchColumnIndex; i++) {
            rowData[i] = row.getObject(i + 1);
        }

        for (; i < originalColumnCount; i++) {
            rowData[i] = row.getObject(i + 1);
        }

        int j = 1;
        for (; i < originalColumnCount + matchColumnIndexOfToAdd - 1; i++, j++) {
            rowData[i] = rowToAdd.getObject(j);
        }
        for (; i < resultColumnCount; i++, j++) {
            rowData[i] = rowToAdd.getObject(j + 1);
        }
        return new CachedRow(rowData);
    }

    private static class CachedRowComparator implements Comparator<CachedRow> {

        private int firstIndex;

        private int secondIndex;

        private boolean isComparable;

        public CachedRowComparator(int firstIndex, int secondIndex,
                boolean isComparable) {
            this.firstIndex = firstIndex;
            this.secondIndex = secondIndex;
            this.isComparable = isComparable;
        }

        public void setFirstIndex(int firstIndex) {
            this.firstIndex = firstIndex;
        }

        public void setComparable(boolean isComparable) {
            this.isComparable = isComparable;
        }

        public void setSecondIndex(int secondIndex) {
            this.secondIndex = secondIndex;
        }

        public int compare(CachedRow object1, CachedRow object2) {
            Object first = object1.getObject(firstIndex);
            Object second = object2.getObject(secondIndex);

            if (first == null && second == null) {
                return 0;
            }

            if (first == null && second != null) {
                return -1;
            }

            if (first != null && second == null) {
                return 1;
            }

            if (isComparable) {
                return ((Comparable<Object>) first).compareTo(second);
            }

            if (first.equals(second)) {
                return 0;
            }
            return -1;
        }
    }
}
TOP

Related Classes of org.apache.harmony.sql.internal.rowset.JoinRowSetImpl$CachedRowComparator

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.