docs: @Override
      public Iterator<Row<K, C>> iterator() {
          return new Iterator<Row<K, C>>() {
              private KeyRange range;
              private org.apache.cassandra.thrift.KeySlice lastRow;
              private List<org.apache.cassandra.thrift.KeySlice> list = null;
              private Iterator<org.apache.cassandra.thrift.KeySlice> iter = null;
              private boolean bContinueSearch = true;
              private boolean bIgnoreTombstones = true;
  
              {
  	            String startToken = query.getStartToken() == null ? partitioner.getMinToken() : query.getStartToken();
  	            String endToken = query.getEndToken() == null ? partitioner.getMaxToken() : query.getEndToken();
  
                  range = new KeyRange()
                          .setCount(query.getBlockSize())
                          .setStart_token(startToken)
                          .setEnd_token(endToken);
                  
                  if (query.getIncludeEmptyRows() == null) {
                      if (query.getPredicate().isSetSlice_range() && query.getPredicate().getSlice_range().getCount() == 0) {
                          bIgnoreTombstones = false;
                      }
                  }
                  else {
                      bIgnoreTombstones = !query.getIncludeEmptyRows();
                  }
              }
  
              @Override
              public boolean hasNext() {
                  // Get the next block
                  while (iter == null || (!iter.hasNext() && bContinueSearch)) {
                      if (lastRow != null) {
                          // Determine the start token for the next page
                          String token = partitioner.getTokenForKey(ByteBuffer.wrap(lastRow.getKey()));
                          if (query.getRepeatLastToken()) {
                              // Start token is non-inclusive
                              range.setStart_token(partitioner.getTokenMinusOne(token));
                          }
                          else {
                              range.setStart_token(token);
                          }
                      }
  
                      // Get the next block of rows from cassandra, exit if none returned
                      list = query.getNextBlock(range);
                      if (list == null || list.isEmpty()) {
                          return false;
                      }
                      
                      // Since we may trim tombstones set a flag indicating whether a complete
                      // block was returned so we can know to try to fetch the next one
                      bContinueSearch = (list.size() == query.getBlockSize());
  
                      // Trim the list from tombstoned rows, i.e. rows with no columns
                      iter = list.iterator();
                      if (iter == null || !iter.hasNext()) {
                          return false;
                      }
  
                      KeySlice previousLastRow = lastRow;
                      lastRow = Iterables.getLast(list);
                      
                      if (query.getRepeatLastToken() && previousLastRow != null) {
                          iter.next();
                          iter.remove();
                      }
                      
                      if (iter.hasNext() && bIgnoreTombstones) {
                          // Discard any tombstones
                          while (iter.hasNext()) {
                              KeySlice row = iter.next();
                              if (row.getColumns().isEmpty()) {
                                  iter.remove();
                              }
                          }
                          
                          // Get the iterator again
                          iter = list.iterator();
                      }
                  }
                  return iter.hasNext();
              }
  
              @Override
              public Row<K, C> next() {
                  org.apache.cassandra.thrift.KeySlice row = iter.next();
                  return new ThriftRowImpl<K, C>(columnFamily.getKeySerializer().fromBytes(row.getKey()),
                          ByteBuffer.wrap(row.getKey()), new ThriftColumnOrSuperColumnListImpl<C>(row.getColumns(),
                                  columnFamily.getColumnSerializer()));
              }
  
              @Override
              public void remove() {
                  throw new IllegalStateException();
              }
          };
      }