blob: 7daf7c113623dc1bc6a0369d3afaa71764737cd5 [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* 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.google.gwt.list.shared;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* An implementation of {@link ListModel}.
*
* @param <T> the data type of records in the list
*/
public abstract class AbstractListModel<T> implements ListModel<T> {
/**
* The range of interest for a single handler.
*/
public static class DefaultRange implements Range, Serializable {
private int start;
private int length;
public DefaultRange(int start, int length) {
this.start = start;
this.length = length;
}
/**
* Used by RPC.
*/
DefaultRange() {
}
public int getLength() {
return length;
}
public int getStart() {
return start;
}
}
/**
* An implementation of {@link ListRegistration} that remembers its own range
* of interest.
*/
private class DefaultListRegistration implements ListRegistration {
private final ListHandler<T> handler;
private int start;
private int length = -1;
/**
* Construct a new {@link DefaultListRegistration}.
*
* @param handler the handler being registered
*/
public DefaultListRegistration(ListHandler<T> handler) {
this.handler = handler;
}
public void removeHandler() {
if (!registrations.contains(this)) {
throw new IllegalStateException("ListHandler has already been removed.");
}
registrations.remove(this);
}
public void setRangeOfInterest(int start, int length) {
this.start = start;
this.length = length;
onRangeChanged();
}
protected ListHandler<T> getHandler() {
return handler;
}
protected int getLength() {
return length;
}
protected int getStart() {
return start;
}
}
/**
* The handlers that are listening to this model.
*/
private List<DefaultListRegistration> registrations = new ArrayList<DefaultListRegistration>();
public ListRegistration addListHandler(ListHandler<T> handler) {
DefaultListRegistration reg = new DefaultListRegistration(handler);
registrations.add(reg);
return reg;
}
/**
* Get the current ranges of all views.
*
* @return the ranges
*/
public Range[] getRanges() {
Range[] ranges = new Range[registrations.size()];
for (int i = 0; i < registrations.size(); i++) {
DefaultListRegistration reg = registrations.get(i);
ranges[i] = new DefaultRange(reg.getStart(), reg.getLength());
}
return ranges;
}
/**
* Called when a view changes its range of interest.
*/
protected abstract void onRangeChanged();
/**
* Inform the views of the total number of items that are available.
*
* @param size the new size
* @param exact true if the size is exact, false if it is a guess
*/
protected void updateDataSize(int size, boolean exact) {
SizeChangeEvent event = new SizeChangeEvent(size, exact);
for (DefaultListRegistration reg : registrations) {
reg.getHandler().onSizeChanged(event);
}
}
/**
* Inform the views of the new data.
*
* @param start the start index
* @param length the length of the data
* @param values the data values
*/
protected void updateViewData(int start, int length, List<T> values) {
int end = start + length;
for (DefaultListRegistration reg : registrations) {
int curStart = reg.getStart();
int curLength = reg.getLength();
int curEnd = curStart + curLength;
if (curStart < end && curEnd > start) {
// Fire the handler with the data that is in the range.
int realStart = curStart < start ? start : curStart;
int realEnd = curEnd > end ? end : curEnd;
int realLength = realEnd - realStart;
List<T> realValues = values.subList(0, realLength);
ListEvent<T> event = new ListEvent<T>(realStart, realLength, realValues);
reg.getHandler().onDataChanged(event);
}
}
}
}