| /* |
| * 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.accumulo.core.client; |
| |
| import java.util.Iterator; |
| import java.util.Map.Entry; |
| import java.util.NoSuchElementException; |
| |
| import org.apache.accumulo.core.data.Key; |
| import org.apache.accumulo.core.data.Value; |
| import org.apache.accumulo.core.util.PeekingIterator; |
| import org.apache.hadoop.io.Text; |
| |
| /** |
| * Group Key/Value pairs into Iterators over rows. Suggested usage: |
| * |
| * <pre> |
| * RowIterator rowIterator = new RowIterator(client.createScanner(tableName, authorizations)); |
| * </pre> |
| */ |
| public class RowIterator implements Iterator<Iterator<Entry<Key,Value>>> { |
| |
| /** |
| * Iterate over entries in a single row. |
| */ |
| private static class SingleRowIter implements Iterator<Entry<Key,Value>> { |
| private PeekingIterator<Entry<Key,Value>> source; |
| private Text currentRow = null; |
| private long count = 0; |
| private boolean disabled = false; |
| |
| /** |
| * SingleRowIter must be passed a PeekingIterator so that it can peek at the next entry to see |
| * if it belongs in the current row or not. |
| */ |
| public SingleRowIter(PeekingIterator<Entry<Key,Value>> source) { |
| this.source = source; |
| if (source.hasNext()) |
| currentRow = source.peek().getKey().getRow(); |
| } |
| |
| @Override |
| public boolean hasNext() { |
| if (disabled) |
| throw new IllegalStateException("SingleRowIter no longer valid"); |
| return currentRow != null; |
| } |
| |
| @Override |
| public Entry<Key,Value> next() { |
| if (disabled) |
| throw new IllegalStateException("SingleRowIter no longer valid"); |
| return _next(); |
| } |
| |
| private Entry<Key,Value> _next() { |
| if (currentRow == null) |
| throw new NoSuchElementException(); |
| count++; |
| Entry<Key,Value> kv = source.next(); |
| if (!source.hasNext() || !source.peek().getKey().getRow().equals(currentRow)) { |
| currentRow = null; |
| } |
| return kv; |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Get a count of entries read from the row (only equals the number of entries in the row when |
| * the row has been read fully). |
| */ |
| public long getCount() { |
| return count; |
| } |
| |
| /** |
| * Consume the rest of the row. Disables the iterator from future use. |
| */ |
| public void consume() { |
| disabled = true; |
| while (currentRow != null) |
| _next(); |
| } |
| } |
| |
| private final PeekingIterator<Entry<Key,Value>> iter; |
| private long count = 0; |
| private SingleRowIter lastIter = null; |
| |
| /** |
| * Create an iterator from an (ordered) sequence of KeyValue pairs. |
| */ |
| public RowIterator(Iterator<Entry<Key,Value>> iterator) { |
| this.iter = new PeekingIterator<>(iterator); |
| } |
| |
| /** |
| * Create an iterator from an Iterable. |
| */ |
| public RowIterator(Iterable<Entry<Key,Value>> iterable) { |
| this(iterable.iterator()); |
| } |
| |
| /** |
| * Returns true if there is at least one more row to get. |
| * |
| * If the last row hasn't been fully read, this method will read through the end of the last row |
| * so it can determine if the underlying iterator has a next row. The last row is disabled from |
| * future use. |
| */ |
| @Override |
| public boolean hasNext() { |
| if (lastIter != null) { |
| lastIter.consume(); |
| count += lastIter.getCount(); |
| lastIter = null; |
| } |
| return iter.hasNext(); |
| } |
| |
| /** |
| * Fetch the next row. |
| */ |
| @Override |
| public Iterator<Entry<Key,Value>> next() { |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| return lastIter = new SingleRowIter(iter); |
| } |
| |
| /** |
| * Unsupported. |
| */ |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Get a count of the total number of entries in all rows read so far. |
| */ |
| public long getKVCount() { |
| return count; |
| } |
| } |