/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.database; import java.util.Iterator; /** * Does a join on two cursors using the specified columns. The cursors must already * be sorted on each of the specified columns in ascending order. This joiner only * supports the case where the tuple of key column values is unique. *
* Typical usage: * *
* CursorJoiner joiner = new CursorJoiner(cursorA, keyColumnsofA, cursorB, keyColumnsofB); * for (CursorJointer.Result joinerResult : joiner) { * switch (joinerResult) { * case LEFT: * // handle case where a row in cursorA is unique * break; * case RIGHT: * // handle case where a row in cursorB is unique * break; * case BOTH: * // handle case where a row with the same key is in both cursors * break; * } * } **/ public final class CursorJoiner implements Iterator
* The caller must check that hasNext() returns true before calling this. *
* Once next() has been called the cursors specified in the result of the call to * next() are guaranteed to point to the row that was indicated. Reading values * from the cursor that was not indicated in the call to next() will result in * undefined behavior. * @return LEFT, if the row pointed to by the left cursor is unique, RIGHT * if the row pointed to by the right cursor is unique, BOTH if the rows in both * cursors are the same. */ public Result next() { if (!hasNext()) { throw new IllegalStateException("you must only call next() when hasNext() is true"); } incrementCursors(); assert hasNext(); boolean hasLeft = !mCursorLeft.isAfterLast(); boolean hasRight = !mCursorRight.isAfterLast(); if (hasLeft && hasRight) { populateValues(mValues, mCursorLeft, mColumnsLeft, 0 /* start filling at index 0 */); populateValues(mValues, mCursorRight, mColumnsRight, 1 /* start filling at index 1 */); switch (compareStrings(mValues)) { case -1: mCompareResult = Result.LEFT; break; case 0: mCompareResult = Result.BOTH; break; case 1: mCompareResult = Result.RIGHT; break; } } else if (hasLeft) { mCompareResult = Result.LEFT; } else { assert hasRight; mCompareResult = Result.RIGHT; } mCompareResultIsValid = true; return mCompareResult; } public void remove() { throw new UnsupportedOperationException("not implemented"); } /** * Reads the strings from the cursor that are specifed in the columnIndicies * array and saves them in values beginning at startingIndex, skipping a slot * for each value. If columnIndicies has length 3 and startingIndex is 1, the * values will be stored in slots 1, 3, and 5. * @param values the String[] to populate * @param cursor the cursor from which to read * @param columnIndicies the indicies of the values to read from the cursor * @param startingIndex the slot in which to start storing values, and must be either 0 or 1. */ private static void populateValues(String[] values, Cursor cursor, int[] columnIndicies, int startingIndex) { assert startingIndex == 0 || startingIndex == 1; for (int i = 0; i < columnIndicies.length; i++) { values[startingIndex + i*2] = cursor.getString(columnIndicies[i]); } } /** * Increment the cursors past the rows indicated in the most recent call to next(). * This will only have an affect once per call to next(). */ private void incrementCursors() { if (mCompareResultIsValid) { switch (mCompareResult) { case LEFT: mCursorLeft.moveToNext(); break; case RIGHT: mCursorRight.moveToNext(); break; case BOTH: mCursorLeft.moveToNext(); mCursorRight.moveToNext(); break; } mCompareResultIsValid = false; } } /** * Compare the values. Values contains n pairs of strings. If all the pairs of strings match * then returns 0. Otherwise returns the comparison result of the first non-matching pair * of values, -1 if the first of the pair is less than the second of the pair or 1 if it * is greater. * @param values the n pairs of values to compare * @return -1, 0, or 1 as described above. */ private static int compareStrings(String... values) { if ((values.length % 2) != 0) { throw new IllegalArgumentException("you must specify an even number of values"); } for (int index = 0; index < values.length; index+=2) { if (values[index] == null) { if (values[index+1] == null) continue; return -1; } if (values[index+1] == null) { return 1; } int comp = values[index].compareTo(values[index+1]); if (comp != 0) { return comp < 0 ? -1 : 1; } } return 0; } }