package org.ovirt.engine.ui.uicommonweb.models; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.ovirt.engine.ui.uicommonweb.HasCleanup; import org.ovirt.engine.ui.uicommonweb.validation.IValidation; import org.ovirt.engine.ui.uicommonweb.validation.ValidationResult; import org.ovirt.engine.ui.uicompat.Event; import org.ovirt.engine.ui.uicompat.EventArgs; import org.ovirt.engine.ui.uicompat.EventDefinition; import org.ovirt.engine.ui.uicompat.IProvideCollectionChangedEvent; import org.ovirt.engine.ui.uicompat.IProvidePropertyChangedEvent; import org.ovirt.engine.ui.uicompat.NotifyCollectionChangedEventArgs; import org.ovirt.engine.ui.uicompat.PropertyChangedEventArgs; import org.ovirt.engine.ui.uicompat.ProvideCollectionChangedEvent; import org.ovirt.engine.ui.uicompat.ProvidePropertyChangedEvent; public class ListModel<T> extends Model { public static final EventDefinition selectedItemChangedEventDefinition; private Event<EventArgs> privateSelectedItemChangedEvent; public Event<EventArgs> getSelectedItemChangedEvent() { return privateSelectedItemChangedEvent; } private void setSelectedItemChangedEvent(Event<EventArgs> value) { privateSelectedItemChangedEvent = value; } public static final EventDefinition selectedItemsChangedEventDefinition; private Event<EventArgs> privateSelectedItemsChangedEvent; public Event<EventArgs> getSelectedItemsChangedEvent() { return privateSelectedItemsChangedEvent; } private void setSelectedItemsChangedEvent(Event<EventArgs> value) { privateSelectedItemsChangedEvent = value; } public static final EventDefinition itemsChangedEventDefinition; private Event<EventArgs> privateItemsChangedEvent; public Event<EventArgs> getItemsChangedEvent() { return privateItemsChangedEvent; } private void setItemsChangedEvent(Event<EventArgs> value) { privateItemsChangedEvent = value; } protected List<T> selectedItems; public List<T> getSelectedItems() { return selectedItems; } public void setSelectedItems(List<T> value) { if (selectedItems != value) { selectedItemsChanging(value, selectedItems); selectedItems = value; selectedItemsChanged(); getSelectedItemsChangedEvent().raise(this, EventArgs.EMPTY); onPropertyChanged(new PropertyChangedEventArgs("SelectedItems")); //$NON-NLS-1$ } } protected T selectedItem; public T getSelectedItem() { return selectedItem; } public void setSelectedItem(T value) { setSelectedItem(value, true); } public void setSelectedItem(T value, boolean fireEvent) { if (selectedItem != value && fireEvent) { onSelectedItemChanging(value, selectedItem); selectedItem = value; onSelectedItemChanged(); getSelectedItemChangedEvent().raise(this, EventArgs.EMPTY); onPropertyChanged(new PropertyChangedEventArgs("SelectedItem")); //$NON-NLS-1$ } } protected Collection<T> items; public Collection<T> getItems() { return items; } public void setItems(Collection<T> value) { setItems(value, null); } public void setItems(Collection<T> value, T selectedItem) { if (items != value) { itemsChanging(value, items); items = value; itemsChanged(selectedItem); getItemsChangedEvent().raise(this, EventArgs.EMPTY); onPropertyChanged(new PropertyChangedEventArgs("Items")); //$NON-NLS-1$ } } private boolean isEmpty; /** * Gets or sets the value indicating whether this model is empty. Notice, that this value is not updated * automatically. */ public boolean getIsEmpty() { return isEmpty; } public void setIsEmpty(boolean value) { if (isEmpty != value) { isEmpty = value; onPropertyChanged(new PropertyChangedEventArgs("IsEmpty")); //$NON-NLS-1$ } } /** * Override this property and return true in order to receive property change notifications for any item but not * only for selected ones. Pay attention, when property change occurs either SelectedItemPropertyChanged or * ItemPropertyChanged will be called but not both of them. */ protected boolean getNotifyPropertyChangeForAnyItem() { return false; } static { selectedItemChangedEventDefinition = new EventDefinition("SelectedItemChanged", ListModel.class); //$NON-NLS-1$ selectedItemsChangedEventDefinition = new EventDefinition("SelectedItemsChanged", ListModel.class); //$NON-NLS-1$ itemsChangedEventDefinition = new EventDefinition("ItemsChanged", ListModel.class); //$NON-NLS-1$ } public ListModel() { setSelectedItemChangedEvent(new Event<>(selectedItemChangedEventDefinition)); setSelectedItemsChangedEvent(new Event<>(selectedItemsChangedEventDefinition)); setItemsChangedEvent(new Event<>(itemsChangedEventDefinition)); } protected void onSelectedItemChanging(T newValue, T oldValue) { } protected void onSelectedItemChanged() { } protected void selectedItemsChanged() { } protected void selectedItemsChanging(List<T> newValue, List<T> oldValue) { // Skip this method when notifying on property change for any // item but not only for selected ones is requested. // Subscribtion to the event will be done in ItemsCollectionChanged method. if (getNotifyPropertyChangeForAnyItem()) { return; } unsubscribeList(oldValue); subscribeList(newValue); } @Override public void eventRaised(Event<? extends EventArgs> ev, Object sender, EventArgs args) { super.eventRaised(ev, sender, args); if (ev.matchesDefinition(ProvidePropertyChangedEvent.definition)) { if (getNotifyPropertyChangeForAnyItem()) { // If notification on property change for any item was requested, // check whether the event was sent by a selected item or not. boolean anyOfSelectedItem = false; if (getSelectedItems() != null) { for (T item : getSelectedItems()) { if (item == sender) { anyOfSelectedItem = true; break; } } } if (anyOfSelectedItem) { selectedItemPropertyChanged(sender, (PropertyChangedEventArgs) args); } else { itemPropertyChanged(sender, (PropertyChangedEventArgs) args); } } else { // In this case a sender always will be a one of selected item. selectedItemPropertyChanged(sender, (PropertyChangedEventArgs) args); } } else if (ev.matchesDefinition(ProvideCollectionChangedEvent.Definition)) { itemsCollectionChanged(sender, (NotifyCollectionChangedEventArgs<T>) args); } } /** * Invoked whenever some property of any selected item was changed. */ protected void selectedItemPropertyChanged(Object sender, PropertyChangedEventArgs e) { } /** * Invoked whenever some property of any item was changed. For performance considerations, in order to get this * method called, override NotifyPropertyChangeForAnyItem property and return true. */ protected void itemPropertyChanged(Object sender, PropertyChangedEventArgs e) { } protected void itemsChanged(T selectedItem) { if (selectedItem == null) { itemsChanged(); } else { List<T> selectedItems = new ArrayList<>(); selectedItems.add(selectedItem); setSelectedItem(selectedItem); setSelectedItems(selectedItems); } } protected void itemsChanged() { // if Items are updated, SelectedItem and SelectedItems become irrelevant: setSelectedItem(null); setSelectedItems(null); } protected void itemsChanging(Collection<T> newValue, Collection<T> oldValue) { IProvideCollectionChangedEvent notifier = (IProvideCollectionChangedEvent) ((oldValue instanceof IProvideCollectionChangedEvent) ? oldValue : null); if (notifier != null) { notifier.getCollectionChangedEvent().removeListener(this); } notifier = (IProvideCollectionChangedEvent) ((newValue instanceof IProvideCollectionChangedEvent) ? newValue : null); if (notifier != null) { notifier.getCollectionChangedEvent().addListener(this); } // Unsure subscribing to the property change notification for all items. unsubscribeList(oldValue); subscribeList(newValue); } /** * Invoked whenever items collection was changed, i.e. some items was added or removed. */ protected void itemsCollectionChanged(Object sender, NotifyCollectionChangedEventArgs<T> e) { if (!getNotifyPropertyChangeForAnyItem()) { return; } // Track property change on all items as necessary. unsubscribeList(e.oldItems); subscribeList(e.newItems); } public void validateSelectedItem(IValidation[] validations) { setIsValid(true); if (!getIsAvailable() || !getIsChangable()) { return; } for (IValidation validation : validations) { ValidationResult result = validation.validate(getSelectedItem()); if (!result.getSuccess()) { for (String reason : result.getReasons()) { getInvalidityReasons().add(reason); } setIsValid(false); break; } } } private void subscribeList(Iterable<T> list) { if (list == null) { return; } for (T a : list) { IProvidePropertyChangedEvent notifier = (IProvidePropertyChangedEvent) ((a instanceof IProvidePropertyChangedEvent) ? a : null); if (notifier != null) { notifier.getPropertyChangedEvent().addListener(this); } } } private void unsubscribeList(Iterable<T> list) { if (list == null) { return; } for (T a : list) { IProvidePropertyChangedEvent notifier = (IProvidePropertyChangedEvent) ((a instanceof IProvidePropertyChangedEvent) ? a : null); if (notifier != null) { notifier.getPropertyChangedEvent().removeListener(this); } } } @Override public void cleanup() { cleanupEvents(getSelectedItemChangedEvent(), getSelectedItemsChangedEvent(), getItemsChangedEvent()); if (getItems() != null) { for (T item : getItems()) { if (item instanceof HasCleanup) { ((HasCleanup) item).cleanup(); } } } super.cleanup(); } }