gt:             if (position >= start && position < end) {
context: /*
   * Copyright (C) 2010 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 com.android.common.widget;
  
  import android.content.Context;
  import android.database.Cursor;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.BaseAdapter;
  
  import java.util.ArrayList;
  
  /**
   * A general purpose adapter that is composed of multiple cursors. It just
   * appends them in the order they are added.
   */
  public abstract class CompositeCursorAdapter extends BaseAdapter {
  
      private static final int INITIAL_CAPACITY = 2;
  
      public static class Partition {
          boolean showIfEmpty;
          boolean hasHeader;
  
          Cursor cursor;
          int idColumnIndex;
          int count;
  
          public Partition(boolean showIfEmpty, boolean hasHeader) {
              this.showIfEmpty = showIfEmpty;
              this.hasHeader = hasHeader;
          }
  
          /**
           * True if the directory should be shown even if no contacts are found.
           */
          public boolean getShowIfEmpty() {
              return showIfEmpty;
          }
  
          public boolean getHasHeader() {
              return hasHeader;
          }
  
          public boolean isEmpty() {
              return count == 0;
          }
      }
  
      private final Context mContext;
      private ArrayList<Partition> mPartitions;
      private int mCount = 0;
      private boolean mCacheValid = true;
      private boolean mNotificationsEnabled = true;
      private boolean mNotificationNeeded;
  
      public CompositeCursorAdapter(Context context) {
          this(context, INITIAL_CAPACITY);
      }
  
      public CompositeCursorAdapter(Context context, int initialCapacity) {
          mContext = context;
          mPartitions = new ArrayList<Partition>();
      }
  
      public Context getContext() {
          return mContext;
      }
  
      /**
       * Registers a partition. The cursor for that partition can be set later.
       * Partitions should be added in the order they are supposed to appear in the
       * list.
       */
      public void addPartition(boolean showIfEmpty, boolean hasHeader) {
          addPartition(new Partition(showIfEmpty, hasHeader));
      }
  
      public void addPartition(Partition partition) {
          mPartitions.add(partition);
          invalidate();
          notifyDataSetChanged();
      }
  
      public void addPartition(int location, Partition partition) {
          mPartitions.add(location, partition);
          invalidate();
          notifyDataSetChanged();
      }
  
      public void removePartition(int partitionIndex) {
          Cursor cursor = mPartitions.get(partitionIndex).cursor;
          if (cursor != null && !cursor.isClosed()) {
              cursor.close();
          }
          mPartitions.remove(partitionIndex);
          invalidate();
          notifyDataSetChanged();
      }
  
      /**
       * Removes cursors for all partitions.
       */
      // TODO: Is this really what this is supposed to do? Just remove the cursors? Not close them?
      // Not remove the partitions themselves? Isn't this leaking?
  
      public void clearPartitions() {
          for (Partition partition : mPartitions) {
              partition.cursor = null;
          }
          invalidate();
          notifyDataSetChanged();
      }
  
      /**
       * Closes all cursors and removes all partitions.
       */
      public void close() {
          for (Partition partition : mPartitions) {
              Cursor cursor = partition.cursor;
              if (cursor != null && !cursor.isClosed()) {
                  cursor.close();
              }
          }
          mPartitions.clear();
          invalidate();
          notifyDataSetChanged();
      }
  
      public void setHasHeader(int partitionIndex, boolean flag) {
          mPartitions.get(partitionIndex).hasHeader = flag;
          invalidate();
      }
  
      public void setShowIfEmpty(int partitionIndex, boolean flag) {
          mPartitions.get(partitionIndex).showIfEmpty = flag;
          invalidate();
      }
  
      public Partition getPartition(int partitionIndex) {
          return mPartitions.get(partitionIndex);
      }
  
      protected void invalidate() {
          mCacheValid = false;
      }
  
      public int getPartitionCount() {
          return mPartitions.size();
      }
  
      protected void ensureCacheValid() {
          if (mCacheValid) {
              return;
          }
  
          mCount = 0;
          for (Partition partition : mPartitions) {
              Cursor cursor = partition.cursor;
              int count;
              if (cursor == null || cursor.isClosed()) {
                  count = 0;
              } else {
                  count = cursor.getCount();
              }
              if (partition.hasHeader) {
                  if (count != 0 || partition.showIfEmpty) {
                      count++;
                  }
              }
              partition.count = count;
              mCount += count;
          }
  
          mCacheValid = true;
      }
  
      /**
       * Returns true if the specified partition was configured to have a header.
       */
      public boolean hasHeader(int partition) {
          return mPartitions.get(partition).hasHeader;
      }
  
      /**
       * Returns the total number of list items in all partitions.
       */
      public int getCount() {
          ensureCacheValid();
          return mCount;
      }
  
      /**
       * Returns the cursor for the given partition
       */
      public Cursor getCursor(int partition) {
          return mPartitions.get(partition).cursor;
      }
  
      /**
       * Changes the cursor for an individual partition.
       */
      public void changeCursor(int partition, Cursor cursor) {
          Cursor prevCursor = mPartitions.get(partition).cursor;
          if (prevCursor != cursor) {
              if (prevCursor != null && !prevCursor.isClosed()) {
                  prevCursor.close();
              }
              mPartitions.get(partition).cursor = cursor;
              if (cursor != null) {
                  mPartitions.get(partition).idColumnIndex = cursor.getColumnIndex("_id");
              }
              invalidate();
              notifyDataSetChanged();
          }
      }
  
      /**
       * Returns true if the specified partition has no cursor or an empty cursor.
       */
      public boolean isPartitionEmpty(int partition) {
          Cursor cursor = mPartitions.get(partition).cursor;
          return cursor == null || cursor.isClosed() || cursor.getCount() == 0;
      }
  
      /**
       * Given a list position, returns the index of the corresponding partition.
       */
      public int getPartitionForPosition(int position) {
          ensureCacheValid();
          int start = 0;
          for (int i = 0, n = mPartitions.size(); i < n; i++) {
              int end = start + mPartitions.get(i).count;
              if (position >= start && position < end) {
                  return i;
              }
              start = end;
          }
          return -1;
      }
  
      /**
       * Given a list position, return the offset of the corresponding item in its
       * partition.  The header, if any, will have offset -1.
       */
      public int getOffsetInPartition(int position) {
          ensureCacheValid();
          int start = 0;
          for (Partition partition : mPartitions) {
              int end = start + partition.count;
              if (position >= start && position < end) {
                  int offset = position - start;
                  if (partition.hasHeader) {
                      offset--;
                  }
                  return offset;
              }
              start = end;
          }
          return -1;
      }
  
      /**
       * Returns the first list position for the specified partition.
       */
      public int getPositionForPartition(int partition) {
          ensureCacheValid();
          int position = 0;
          for (int i = 0; i < partition; i++) {
              position += mPartitions.get(i).count;
          }
          return position;
      }
  
      @Override
      public int getViewTypeCount() {
          return getItemViewTypeCount() + 1;
      }
  
      /**
       * Returns the overall number of item view types across all partitions. An
       * implementation of this method needs to ensure that the returned count is
       * consistent with the values returned by {@link #getItemViewType(int,int)}.
       */
      public int getItemViewTypeCount() {
          return 1;
      }
  
      /**
       * Returns the view type for the list item at the specified position in the
       * specified partition.
       */
      protected int getItemViewType(int partition, int position) {
          return 1;
      }
  
      @Override
      public int getItemViewType(int position) {
          ensureCacheValid();
          int start = 0;
          for (int i = 0, n = mPartitions.size(); i < n; i++) {
              int end = start  + mPartitions.get(i).count;
              if (position >= start && position < end) {
                  int offset = position - start;
                  if (mPartitions.get(i).hasHeader) {
                      offset--;
                  }
                  if (offset == -1) {
                      return IGNORE_ITEM_VIEW_TYPE;
                  } else {
                      return getItemViewType(i, offset);
                  }
              }
              start = end;
          }
  
          throw new ArrayIndexOutOfBoundsException(position);
      }
  
      public View getView(int position, View convertView, ViewGroup parent) {
          ensureCacheValid();
          int start = 0;
          for (int i = 0, n = mPartitions.size(); i < n; i++) {
              int end = start + mPartitions.get(i).count;
  