package org.ovirt.engine.ui.userportal.client.components;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import org.ovirt.engine.core.compat.Event;
import org.ovirt.engine.core.compat.EventArgs;
import org.ovirt.engine.core.compat.IEventListener;
import org.ovirt.engine.core.compat.PropertyChangedEventArgs;
import org.ovirt.engine.ui.uicommon.models.SearchableListModel;
import org.ovirt.engine.ui.userportal.client.events.SelectedItemChangedEventArgs;
public abstract class GridController<T> implements IEventListener {
private HashMap<Object,GridElement<T>> gridElements = new HashMap<Object,GridElement<T>>();
protected GridElement<T> selectedElement;
private SearchableListModel model;
private GridTimer searchTimer;
private Event selectionChangedEvent = new Event("SelectionChanged", GridController.class);
private Event itemsChangedEvent = new Event("RefreshSelectedItemDetails", GridController.class);
private SelectedItemChangedEventArgs eventArgs = new SelectedItemChangedEventArgs(null);
// Event that is raised upon changed selection status (Initial item was selected / Selected item was de-selected)
private Event selectionStatusChangedEvent = new Event("SelectionStatusChanged", GridController.class);
public static PropertyChangedEventArgs ITEM_SELECTED_EVENT_ARGS = new PropertyChangedEventArgs("ItemSelected");
public static PropertyChangedEventArgs NO_ITEM_SELECTED_EVENT_ARGS = new PropertyChangedEventArgs("NoItemSelected");
private static String GRID_REFRESH_TIMER_NAME = "GRID_REFRESH_TIMER_";
private static int MIN_REFRESH_INTERVAL_IN_MILLIS = 2000;
private static int MAX_REFRESH_INTERVAL_IN_MILLIS = 8000;
private static int[] REPETITIONS_PER_INTERVAL = new int[] { 3, 30, 3 };
private static int INTERVAL_MULTIPLIER = 2;
private int repetitionCounter = 0;
private String gridName = this.getClass().getName();
private boolean isRapidTimerRunning = false;
private boolean selectDefaultValue = false;
// Grid's refresh rate (in seconds)
// Each controller holds this value for supporting different refresh rates for every grid
// [not needed now, but might be requested]
private int refreshRate = GridRefreshManager.getInstance().getGlobalRefreshRate();
protected void handleResults(Iterable<T> items) {
int position = 0;
ArrayList<Object> existingVMs = new ArrayList<Object>();
for (T item : items) {
Object itemId = getId(item);
// Add the item to the list of existing items to check for removed ones later
existingVMs.add(itemId);
// Set the selected item again after each search since the search initializes it
if(selectedElement != null)
if (itemId.equals(selectedElement.getItemId())) {
model.setSelectedItem(getSelectedItem());
model.setSelectedItems(Arrays.asList(getSelectedItem()));
}
// If the item doesn't exist in the grid, add it to the grid and create the component
if (!gridElements.keySet().contains(itemId)) {
gridElements.put(itemId, addItem(item, position));
}
else { // The item exist, check for updates in the values
gridElements.get(itemId).updateValues(item);
}
position++;
}
// Compare the list of items in the grid to the items returned from the search to check if items were removed
ArrayList<Object> elementsToRemove = new ArrayList<Object>();
for (Object id : gridElements.keySet()) {
if (!existingVMs.contains(id)) {
elementsToRemove.add(id);
// If the removed item was the selected item, deselect it
if (selectedElement != null && selectedElement.equals(gridElements.get(id)))
select(null);
}
}
for (Object id : elementsToRemove) {
GWT.log("Removing item with id : " + id);
removeItem(gridElements.get(id));
gridElements.remove(id);
}
if (selectDefaultValue && selectedElement == null && !gridElements.isEmpty()) {
select(gridElements.get(getId(items.iterator().next())));
}
}
public void select(GridElement<T> elementToSelect) {
if (selectedElement != null) {
// Abort if the item to select is already select
if (selectedElement.equals(elementToSelect))
return;
selectedElement.deselect();
}
else {
// Initial item selected, raise selection status changed event
selectionStatusChangedEvent.raise(this, ITEM_SELECTED_EVENT_ARGS);
}
selectedElement = elementToSelect;
if (selectedElement != null) {
selectedElement.select();
}
else {
// Selection changed to null, raise selection status changed event
selectionStatusChangedEvent.raise(this, NO_ITEM_SELECTED_EVENT_ARGS);
}
T item = selectedElement == null ? null : getItemById(selectedElement.getItemId());
model.setSelectedItem(item);
ArrayList<T> items = new ArrayList<T>();
items.add(item);
model.setSelectedItems(items);
eventArgs.selectedItem = item;
selectionChangedEvent.raise(this, eventArgs);
}
private T getItemById(Object id) {
if (id == null)
return null;
for (T item : (List<T>)model.getItems()) {
if (getId(item).equals(id)) {
return item;
}
}
return null;
}
long d;
public void search() {
model.Search();
}
public void repeatSearch(int intervalInMillis) {
repeatSearch(intervalInMillis, intervalInMillis, 1);
}
@SuppressWarnings("unchecked")
final public void repeatSearch(int intervalInMillis, final int maxIntervalInMillis, final int intervalMultiplier) {
if (searchTimer != null) {
searchTimer.cancel();
}
if (intervalInMillis == MIN_REFRESH_INTERVAL_IN_MILLIS) {
repetitionCounter = 0;
}
searchTimer = (GridTimer)UserPortalTimerFactory.factoryTimer(GRID_REFRESH_TIMER_NAME + model.getClass().getName(), new GridTimer() {
@Override
public void run() {
GWT.log("Invoking grid timer for the grid " + gridName + ", intervalInMillis:" + intervalInMillis + ", has repetitions: " + hasRepetitions + (hasRepetitions ? ", remaining repetitions: " + repetitions : ""));
// Handle timer with repetitions
if (hasRepetitions) {
repetitions--;
if (repetitions < 1) {
// Cancel current grid timer
cancel();
// Schedule another repetition with a higher interval
repeatSearch(intervalInMillis * intervalMultiplier, maxIntervalInMillis, intervalMultiplier);
}
}
search();
}
});
// Set timer's repetitions (if needed)
int repetitionsPerInterval = REPETITIONS_PER_INTERVAL[repetitionCounter];
if (repetitionsPerInterval > 0) {
searchTimer.repetitions = repetitionsPerInterval;
searchTimer.hasRepetitions = true;
if (repetitionCounter == REPETITIONS_PER_INTERVAL.length - 1) repetitionCounter = 0;
else repetitionCounter++;
}
else {
searchTimer.hasRepetitions = false;
}
// Set timer's interval
searchTimer.intervalInMillis = intervalInMillis;
// Start timer (if needed)
if (intervalInMillis <= maxIntervalInMillis) {
searchTimer.scheduleRepeating(intervalInMillis);
if (intervalMultiplier > 1) isRapidTimerRunning = true;
}
else if (GridRefreshManager.getInstance().isSubscribed(this)) {
// If this controller is subscribed to refresh manager,
// start repeat search again with its defined refresh rate
isRapidTimerRunning = false;
GridRefreshManager.getInstance().refreshGrids();
}
}
public void stopRepeatedSearch() {
if (searchTimer != null)
searchTimer.cancel();
}
public boolean isRapidTimerRunning() {
return isRapidTimerRunning;
}
// Should be called whenever an action that has an impact on the grid was performed
public void gridChangePerformed() {
GridRefreshManager.getInstance().suspendRefresh();
repeatSearch(MIN_REFRESH_INTERVAL_IN_MILLIS, MAX_REFRESH_INTERVAL_IN_MILLIS, INTERVAL_MULTIPLIER);
}
public void setModel(SearchableListModel model) {
this.model = model;
model.getItemsChangedEvent().addListener(this);
}
public void eventRaised(Event ev, Object sender, EventArgs args) {
handleResults(model.getItems());
itemsChangedEvent.raise(this, EventArgs.Empty);
}
public T getSelectedItem() {
if (selectedElement != null) {
return getItemById(selectedElement.getItemId());
}
return null;
}
public GridElement<T> getSelectedItemLayout() {
if (selectedElement != null) {
return gridElements.get(selectedElement.getItemId());
}
return null;
}
public Event getSelectionChangedEvent() {
return selectionChangedEvent;
}
public Event getItemsChangedEvent() {
return itemsChangedEvent;
}
public Event getSelectionStatusChangedEvent() {
return selectionStatusChangedEvent;
}
public void setSelectDefaultValue(boolean selectDefaultValue) {
this.selectDefaultValue = selectDefaultValue;
}
public int getRefreshRate() {
return refreshRate;
}
public void setRefreshRate(int refreshRate) {
this.refreshRate = refreshRate;
}
public abstract Object getId(T item);
public abstract GridElement<T> addItem(T item, int position);
public abstract void removeItem(GridElement<T> itemView);
abstract class GridTimer extends Timer {
public int intervalInMillis;
public int repetitions;
public boolean hasRepetitions;
}
}