private void setVisibleRange(final Range range, final boolean clearData, final boolean forceRangeChangeEvent) {
        final int start = range.getStart();
        final int length = range.getLength();
        if (start < 0) {
            throw new IllegalArgumentException("Range start cannot be less than 0");
        }
        if (length < 0) {
            throw new IllegalArgumentException("Range length cannot be less than 0");
        }

        // Update the page start.
        final int pageStart = getPageStart();
        final int pageSize = getPageSize();
        final boolean pageStartChanged = (pageStart != start);
        if (pageStartChanged) {
            final PendingState<T> pending = ensurePendingState();

            // Trim the data if we aren't clearing it.
            if (!clearData) {
                if (start > pageStart) {
                    final int increase = start - pageStart;
                    if (getVisibleItemCount() > increase) {
                        // Remove the data we no longer need.
                        for (int i = 0; i < increase; i++) {
                            pending.rowData.remove(0);
                        }
                    } else {
                        // We have no overlapping data, so just clear it.
                        pending.rowData.clear();
                    }
                } else {
                    final int decrease = pageStart - start;
                    if ((getVisibleItemCount() > 0) && (decrease < pageSize)) {
                        // Insert null data at the beginning.
                        for (int i = 0; i < decrease; i++) {
                            pending.rowData.add(0, null);
                        }

                        // Remember the inserted range because we might return
                        // to the same
                        // pageStart in this event loop, which means we won't do
                        // a full
                        // redraw, but still need to replace the inserted nulls
                        // in the view.
                        pending.replaceRange(start, start + decrease);
                    } else {
                        // We have no overlapping data, so just clear it.
                        pending.rowData.clear();
                    }
                }
            }

            // Update the page start.
            pending.pageStart = start;
        }

        // Update the page size.
        final boolean pageSizeChanged = (pageSize != length);
        if (pageSizeChanged) {
            ensurePendingState().pageSize = length;
        }

        // Clear the data.
        if (clearData) {
            ensurePendingState().rowData.clear();
        }

        // Trim the row values if needed.
        updateCachedData();

        // Update the pager and data source if the range changed.
        if (pageStartChanged || pageSizeChanged || forceRangeChangeEvent) {
            RangeChangeEvent.fire(display, getVisibleRange());
        }
    }
Set the visible {@link Range}, optionally clearing data and/or firing a
{@link RangeChangeEvent}.

@param range                 the new {@link Range}
@param clearData             true to clear all data
@param forceRangeChangeEvent true to force a {@link RangeChangeEvent}
stroom-core-client-widget/src/main/java/com/google/gwt/user/cellview/client/HasDataPresenter.java