Package com.sencha.gxt.data.shared

Source Code of com.sencha.gxt.data.shared.Store$StoreSortInfo

/**
* Sencha GXT 3.1.0-beta - Sencha for GWT
* Copyright(c) 2007-2014, Sencha, Inc.
* licensing@sencha.com
*
* http://www.sencha.com/products/gxt/license/
*/
package com.sencha.gxt.data.shared;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.shared.FastMap;
import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration;
import com.sencha.gxt.data.shared.event.StoreAddEvent;
import com.sencha.gxt.data.shared.event.StoreAddEvent.StoreAddHandler;
import com.sencha.gxt.data.shared.event.StoreClearEvent;
import com.sencha.gxt.data.shared.event.StoreClearEvent.StoreClearHandler;
import com.sencha.gxt.data.shared.event.StoreDataChangeEvent;
import com.sencha.gxt.data.shared.event.StoreDataChangeEvent.StoreDataChangeHandler;
import com.sencha.gxt.data.shared.event.StoreFilterEvent;
import com.sencha.gxt.data.shared.event.StoreFilterEvent.StoreFilterHandler;
import com.sencha.gxt.data.shared.event.StoreHandlers;
import com.sencha.gxt.data.shared.event.StoreHandlers.HasStoreHandlers;
import com.sencha.gxt.data.shared.event.StoreRecordChangeEvent;
import com.sencha.gxt.data.shared.event.StoreRecordChangeEvent.StoreRecordChangeHandler;
import com.sencha.gxt.data.shared.event.StoreRemoveEvent;
import com.sencha.gxt.data.shared.event.StoreRemoveEvent.StoreRemoveHandler;
import com.sencha.gxt.data.shared.event.StoreSortEvent;
import com.sencha.gxt.data.shared.event.StoreSortEvent.StoreSortHandler;
import com.sencha.gxt.data.shared.event.StoreUpdateEvent;
import com.sencha.gxt.data.shared.event.StoreUpdateEvent.StoreUpdateHandler;

/**
* Store is a client-side cache for collections of data. Modifications made to
* the Store via Records are not passed right away to the data, allowing for the
* changes to be committed or rolled back.
*
* @param <M> the model type
*/
public abstract class Store<M> implements HasStoreHandlers<M> {
  /**
   * Represents a change that can occur to a given model. This interface may not
   * be required, it will depend on if legacy cases need it or not to allow
   * PropertyChange to be implemented another way
   *
   * @param <M> the model type
   * @param <V> the value type (for the changed property in the model)
   */
  public interface Change<M, V> {
    /**
     * Gets a tag for this change, so that two changes, both making
     * modifications to the same field, can replace each other, as they must be
     * mutually exclusive
     *
     * @return the tag
     */
    Object getChangeTag();

    /**
     * Gets the value that will be set on the model in modify(M).
     *
     * @return the value
     */
    V getValue();

    /**
     * Checks to see if the given model already has the change
     *
     * @param model the model
     * @return true if model already has the change
     */
    boolean isCurrentValue(M model);

    /**
     * Make the change recorded here to the given model
     *
     * @param model the model
     */
    void modify(M model);
  }

  /**
   * ValueProvider-based change impl - takes a ValueProvider and the new value
   * to be changed. The ValueProvider instance should be reused, as it will be
   * the objecttag.
   *
   * @param <M> the model type
   * @param <V> the value type (for the changed property in the model)
   */
  public static class PropertyChange<M, V> implements Change<M, V> {
    private final ValueProvider<? super M, V> access;
    private final V value;

    /**
     * Creates a new property change.
     *
     * @param propertyAccess the changed property
     * @param value the changed value
     */
    public PropertyChange(ValueProvider<? super M, V> propertyAccess, V value) {
      access = propertyAccess;
      this.value = value;
    }

    public final Object getChangeTag() {
      return access.getPath();
    }

    public V getValue() {
      return value;
    }

    public boolean isCurrentValue(M model) {
      return value == null ? access.getValue(model) == null : value.equals(access.getValue(model));
    }

    public final void modify(M model) {
      access.setValue(model, value);
    }
  }

  /**
   * Records wrap model instances and provide specialized editing features,
   * including modification tracking and editing capabilities.
   */
  public class Record {

    private final M model;
    private final Map<Object, Change<M, ?>> changes = new HashMap<Object, Store.Change<M, ?>>();

    /**
     * Creates a new record that wraps the given model.
     *
     * @param model the model to be wrapped by this record
     */
    public Record(M model) {
      this.model = model;
    }

    /**
     * Adds a change to the data in this Record. If auto commit is enabled, the
     * change will be made directly to the model, else the change will be queued
     * up until commit() is called.
     *
     * @param <V> the value type (for the changed property in the model)
     * @param property the property to change
     * @param value the changed value
     */
    public <V> void addChange(ValueProvider<? super M, V> property, V value) {
      if (!isAutoCommit) {
        Change<M, V> c = new PropertyChange<M, V>(property, value);
        if (c.isCurrentValue(model)) {
          changes.remove(c.getChangeTag());
          if (changes.size() == 0) {
            modifiedRecords.remove(this);
          }
        } else {
          changes.put(c.getChangeTag(), c);
          modifiedRecords.add(this);
        }
        fireEvent(new StoreRecordChangeEvent<M>(this, property));
      } else {
        property.setValue(model, value);
        fireEvent(new StoreUpdateEvent<M>(Collections.singletonList(this.model)));
      }
    }

    /**
     * Commits the changes to the model tracked by this record.
     */
    public void commit(boolean fireEvent) {
      if (isDirty()) {
        for (Change<M, ?> c : changes.values()) {
          assert c.isCurrentValue(model) == false : "Current value was somehow stored in a record's change set!";
          c.modify(model);
        }
        changes.clear();
        if (fireEvent) {
          fireEvent(new StoreUpdateEvent<M>(Collections.singletonList(this.model)));
        }
      }
    }

    /**
     * Gets the current Change object applied to that property, if any.
     *
     * @param <V> the value type (for the changed property in the model)
     * @param property the changed property
     * @return a Change object, or null if the value is the default
     */
    @SuppressWarnings("unchecked")
    public <V> Change<M, V> getChange(ValueProvider<? super M, V> property) {
      // This will be typesafe ONLY if only addChange(ValueProvider<M,V>, V) is
      // called
      // if we keep this, kill the other addChange, or the Change interface
      // itself
      return (Change<M, V>) changes.get(property.getPath());
    }

    /**
     * Returns all changes.
     *
     * @return collection of the changes
     */
    public Collection<Change<M, ?>> getChanges() {
      return changes.values();
    }

    /**
     * Returns the wrapped model instance.
     *
     * @return the model
     */
    public M getModel() {
      return model;
    }

    /**
     * Gets the current value of this property in the record, whether it is
     * saved or not.
     *
     * The value on the model in this record can be obtained by calling
     * property.getValue(record.getModel())
     *
     * @param <V> the value type (for the property in the model)
     * @param property the property containing the value to get
     * @return current value of this property
     */
    public <V> V getValue(ValueProvider<? super M, V> property) {
      Change<M, V> change = getChange(property);
      if (change == null) {
        return property.getValue(model);
      } else {
        return change.getValue();
      }
    }

    /**
     * Returns true if the record has uncommitted changes.
     *
     * @return the dirty state
     */
    public boolean isDirty() {
      return !changes.isEmpty();
    }

    /**
     * Rejects a single change made to the Record since its creation, or since
     * the last commit operation.
     *
     * Fires a {@link StoreUpdateEvent} if a change is made.
     *
     * @param property the property of the model to revert
     */
    public void revert(ValueProvider<? super M,?> property) {
      if (changes.remove(property.getPath()) != null) {
        fireEvent(new StoreUpdateEvent<M>(Collections.singletonList(this.model)));
      }
    }

    /**
     * Rejects all changes made to the Record since either creation, or the last
     * commit operation. Modified fields are reverted to their original values.
     */
    public void revert() {
      changes.clear();

      fireEvent(new StoreUpdateEvent<M>(Collections.singletonList(this.model)));
    }
  }

  /**
   * Defines the interface for store filters.
   *
   * Filters receive only the last stored version of data, in contrast to 2.x,
   * where the current changed value was always available. To get the change
   * value, ask the Store for a Record instance.
   *
   * @param <M> the model type
   */
  public interface StoreFilter<M> {
    /**
     * Indicates if the given item should be kept visible in the store. If false
     * is returned, the item will not be visible, and if true is returned, the
     * item may be visible, pending any other filter's decision.
     *
     * @param store the store containing the item to be kept visible
     * @param parent the parent of the item (for hierarchical stores)
     * @param item the item to keep visible
     * @return true if the item will be visible, false if not
     */
    public boolean select(Store<M> store, M parent, M item);
  }

  /**
   * Sort information for a Store to use. Constructors make it possible to
   * easily sort based on either a property of the items in the store, or sort
   * the items themselves.
   *
   * Sort direction may be changed after creation, but the comparator is fixed.
   * A new StoreSortInfo object must be created to change the comparator.
   *
   * @param <M> the model type
   */
  public static class StoreSortInfo<M> implements Comparator<M> {
    private SortDir direction;
    private final Comparator<? super M> comparator;
    private final ValueProvider<? super M, ?> valueProvider;

    /**
     * Creates a sort info object based on the given comparator to act on the
     * item itself. Complex comparators can easily be built in this way, instead
     * of adding multiple StoreSortInfo objects, or using one of the other
     * constructors.
     *
     * @param itemComparator the comparator to use to sort the items
     * @param direction the sort direction
     */
    public StoreSortInfo(Comparator<? super M> itemComparator, SortDir direction) {
      this.comparator = itemComparator;
      this.direction = direction;
      this.valueProvider = null;
    }

    /**
     * Creates a sort info object to act on a property of the items and a custom
     * comparator for that property's type.
     *
     * @param <V> the property type
     * @param property the sort property
     * @param itemComparator the comparator to use in the sort
     * @param direction the sort direction
     */
    public <V> StoreSortInfo(final ValueProvider<? super M, V> property, final Comparator<? super V> itemComparator, SortDir direction) {
      this.valueProvider = property;
      this.direction = direction;
      this.comparator = new Comparator<M>() {
        public int compare(M o1, M o2) {
          return itemComparator.compare(property.getValue(o1), property.getValue(o2));
        }
      };
    }

    /**
     * Convenience constructor for sorting based on a {@link Comparable}
     * property of items in the store.
     *
     * @param <V> the property type
     * @param property the sort property
     * @param direction the sort direction
     */
    public <V extends Comparable<V>> StoreSortInfo(final ValueProvider<? super M, V> property, SortDir direction) {
      this.valueProvider = property;
      this.direction = direction;
      this.comparator = new Comparator<M>() {
        public int compare(M o1, M o2) {
          V v1 = property.getValue(o1);
          V v2 = property.getValue(o2);
          if ((v1 == null & v2 != null) || (v1 != null && v2 == null)) {
            return v1 == null ? -1 : 1;
          }
          if (v1 == null & v2 == null) {
            return 0;
          }
          return v1.compareTo(v2);
        }
      };
    }

    @Override
    public int compare(M o1, M o2) {
      int val = comparator.compare(o1, o2);
      return direction == SortDir.ASC ? val : -val;
    }

    /**
     * Returns the current sort direction for this sort info.
     *
     * @return the current sort direction
     */
    public SortDir getDirection() {
      return direction;
    }

    /**
     * If the sort info object is configured to act on a property of the items,
     * returns the path that the property's ValueProvider makes available,
     * otherwise returns empty string.
     *
     * @return the path for the property value provider or empty string if no
     *         value provider is configured
     */
    public String getPath() {
      return valueProvider != null ? valueProvider.getPath() : "";
    }

    /**
     * Returns the sort property's ValueProvider.
     *
     * @return the sort property's ValueProvider or null if one has not been
     *         configured
     */
    public ValueProvider<? super M, ?> getValueProvider() {
      return valueProvider;
    }

    /**
     * Sets a new sort direction. Will not take effect until
     * {@link Store#applySort(boolean)} is called on the store containing the
     * sort info.
     *
     * @param direction the sort direction
     */
    public void setDirection(SortDir direction) {
      this.direction = direction;
    }
  }

  // TODO lazily init these?
  private final Map<String, Record> records = new FastMap<Record>();
  private Set<Record> modifiedRecords = new HashSet<Record>();
  private boolean isAutoCommit = false;
  private ModelKeyProvider<? super M> keyProvider;
  private List<StoreSortInfo<M>> comparators = new ArrayList<StoreSortInfo<M>>();
  private HandlerManager handlerManager;
  private boolean filtersEnabled;

  /**
   * Using a LinkedHashSet so each filter can only be added once, and order
   * matters
   */
  private LinkedHashSet<StoreFilter<M>> filters;

  /**
   * Creates a store with the given key provider. The key provider is
   * responsible for returning a unique key for a given model
   *
   * @param keyProvider the key provider, responsible for returning a unique key
   *          for a given model
   */
  public Store(ModelKeyProvider<? super M> keyProvider) {
    this.keyProvider = keyProvider;
  }

  /**
   * Adds the filter to the end of the store's set of filters. Runs the filters
   * again if they are enabled.
   *
   * @param filter the filter to add
   */
  public void addFilter(StoreFilter<M> filter) {
    if (filters == null) {
      filters = new LinkedHashSet<Store.StoreFilter<M>>();
    }
    filters.add(filter);

    if (filtersEnabled) {
      // TODO consider not running the full set of filters, just limiting what
      // is already visible
      applyFilters();
    }
  }

  /**
   * Adds the sort info at the specified index. The store will be sorted after
   * this change.
   *
   * @param index the sort index
   * @param info the sort info
   */
  public void addSortInfo(int index, StoreSortInfo<M> info) {
    comparators.add(index, info);
    applySort(false);
  }

  /**
   * Adds the specified sort info to the store. The store will then be sorted
   * based on this new sort info.
   *
   * @param info the sort info
   */
  public void addSortInfo(StoreSortInfo<M> info) {
    comparators.add(info);
    applySort(false);
  }

  @Override
  public HandlerRegistration addStoreAddHandler(StoreAddHandler<M> handler) {
    return ensureHandlers().addHandler(StoreAddEvent.getType(), handler);
  }

  @Override
  public HandlerRegistration addStoreClearHandler(StoreClearHandler<M> handler) {
    return ensureHandlers().addHandler(StoreClearEvent.getType(), handler);
  }

  @Override
  public HandlerRegistration addStoreDataChangeHandler(StoreDataChangeHandler<M> handler) {
    return ensureHandlers().addHandler(StoreDataChangeEvent.getType(), handler);
  }

  @Override
  public HandlerRegistration addStoreFilterHandler(StoreFilterHandler<M> handler) {
    return ensureHandlers().addHandler(StoreFilterEvent.getType(), handler);
  }

  @Override
  public HandlerRegistration addStoreHandlers(StoreHandlers<M> handlers) {
    GroupingHandlerRegistration reg = new GroupingHandlerRegistration();
    reg.add(addStoreAddHandler(handlers));
    reg.add(addStoreRemoveHandler(handlers));
    reg.add(addStoreClearHandler(handlers));
    reg.add(addStoreDataChangeHandler(handlers));
    reg.add(addStoreFilterHandler(handlers));
    reg.add(addStoreUpdateHandler(handlers));
    reg.add(addStoreRecordChangeHandler(handlers));
    reg.add(addStoreSortHandler(handlers));
    return reg;
  }

  @Override
  public HandlerRegistration addStoreRecordChangeHandler(StoreRecordChangeHandler<M> handler) {
    return ensureHandlers().addHandler(StoreRecordChangeEvent.getType(), handler);
  }

  @Override
  public HandlerRegistration addStoreRemoveHandler(StoreRemoveHandler<M> handler) {
    return ensureHandlers().addHandler(StoreRemoveEvent.getType(), handler);
  }

  @Override
  public HandlerRegistration addStoreSortHandler(StoreSortHandler<M> handler) {
    return ensureHandlers().addHandler(StoreSortEvent.getType(), handler);
  }

  @Override
  public HandlerRegistration addStoreUpdateHandler(StoreUpdateHandler<M> handler) {
    return ensureHandlers().addHandler(StoreUpdateEvent.getType(), handler);
  }

  /**
   * Tells the store to re-apply sort settings and to fire an event when
   * complete. Must be called when manipulating the sort settings directly
   * instead of through the store, or the sort order will not change.
   *
   * Automatically called after {@link #addSortInfo(StoreSortInfo)},
   * {@link #addSortInfo(int, StoreSortInfo)}, and {@link #clearSortInfo()}.
   *
   * @param suppressEvent true to suppress event from firing
   */
  public abstract void applySort(boolean suppressEvent);

  /**
   * Removes all of the sort info from the store, so subsequent calls to
   * applySort will not change the order.
   */
  public void clearSortInfo() {
    comparators.clear();
  }

  /**
   * Commits the outstanding changes.
   */
  public void commitChanges() {
    List<M> committedData = new ArrayList<M>();
    for (Record r : modifiedRecords) {
      r.commit(false);
      committedData.add(r.getModel());
    }
    modifiedRecords.clear();
    fireEvent(new StoreUpdateEvent<M>(committedData));
  }

  /**
   * Finds the matching model using the store's key provider. This can be used
   * to check if an item is present in the store, as it will return null if not.
   *
   * @param model target model
   * @return the matching model or null if the model is not present
   */
  public M findModel(M model) {
    return findModelWithKey(getKeyProvider().getKey(model));
  }

  /**
   * Finds the model with the given key, using {@link ModelKeyProvider} as
   * necessary.
   *
   * @param key the key of the model to find
   * @return the model with the given key, or null if the model cannot be found
   *         in the store
   */
  public abstract M findModelWithKey(String key);

  public void fireEvent(GwtEvent<?> event) {
    if (handlerManager != null) {
      handlerManager.fireEvent(event);
    }
  }

  /**
   * Returns a list of all items contained in the store. Modifying this list
   * will not change the store, as this is a copy of the contents of the store.
   * Note also that because this is a copy, this can be a expensive call to
   * make.
   *
   * @return the list of items
   */
  public abstract List<M> getAll();

  /**
   * Returns the stores filters.
   *
   * @return the filters
   */
  public LinkedHashSet<StoreFilter<M>> getFilters() {
    return filters;
  }

  /**
   * Returns the stores model key provider.
   *
   * @return the model key provider
   */
  public ModelKeyProvider<? super M> getKeyProvider() {
    return keyProvider;
  }

  /**
   * Returns a list of records that have been changed and not committed.
   *
   * @return the list of modified records
   */
  public Collection<Store<M>.Record> getModifiedRecords() {
    return Collections.unmodifiableCollection(modifiedRecords);
  }

  /**
   * Gets the current Record instance for the given item. If a Record doesn't already exist, one
   * will be created. Use {@link #hasRecord(Object)} to check if you don't want to create a new one.
   *
   * @param data the data key
   * @return the record
   */
  public Record getRecord(M data) {
    String key = getKeyProvider().getKey(data);
    Record rec = records.get(key);
    if (rec == null) {
      rec = new Record(data);
      records.put(key, rec);
    }
    return rec;
  }

  /**
   * Gets the list of sort info objects. This list may be modified directly, but
   * before it takes effect, {@link #applySort(boolean)} must be called. Note
   * that {@link #addSortInfo(StoreSortInfo)} and
   * {@link #addSortInfo(int, StoreSortInfo)} will add the new sort info the
   * this list, and then call applySort directly.
   *
   * @return the current mutable list of StoreSortInfo instances
   */
  public List<StoreSortInfo<M>> getSortInfo() {
    return comparators;
  }

  /**
   * Returns true if the two models have the same key.
   *
   * @param model1 the first model
   * @param model2 the second model
   * @return true if equals
   */
  public boolean hasMatchingKey(M model1, M model2) {
    return keyProvider.getKey(model1).equals(keyProvider.getKey(model2));
  }

  /**
   * Returns true if a record exists for the given model.
   *
   * @param data the model
   * @return true if a record exists
   */
  public boolean hasRecord(M data) {
    return records.get(getKeyProvider().getKey(data)) != null;
  }

  /**
   * Returns true if auto commit is enabled.
   *
   * @return true if auto commit is enabled
   */
  public boolean isAutoCommit() {
    return isAutoCommit;
  }

  /**
   * Returns true if filtering is enabled, whether or not filters are present.
   *
   * @return true if filtering is enabled
   */
  public boolean isEnableFilters() {
    return filtersEnabled;
  }

  /**
   * Returns true if filtering is enabled AND the store has filters.
   *
   * @return true if the store is filtered
   */
  public boolean isFiltered() {
    return filtersEnabled && filters != null && filters.size() != 0;
  }

  /**
   * Cancel outstanding changes on all changed records.
   */
  public void rejectChanges() {
    for (Record r : modifiedRecords) {
      r.revert();
    }
    modifiedRecords.clear();
  }

  /**
   * Removes the filter from the store's set of filters. Runs the filters again
   * if they are enabled, and the filter was actually in the list.
   *
   * @param filter the filter to be removed
   */
  public void removeFilter(StoreFilter<M> filter) {
    if (filters != null) {
      if (filters.remove(filter) && filtersEnabled) {
        // the list of active filters has changed, unless we cache what showed
        // what item, we need to re-run the whole set
        applyFilters();
      }
    }
  }

  /**
   * Removes all filters.
   */
  public void removeFilters() {
    if (filters != null) {
      filters.clear();
      applyFilters();
    }
  }

  /**
   * Enables or disables auto commit. If auto commit is enabled, the change will
   * be made directly to the model, else the change will be queued up until
   * commit() is called.
   *
   * @param isAutoCommit true to enable auto commit
   * @see Record#addChange(ValueProvider, Object)
   */
  public void setAutoCommit(boolean isAutoCommit) {
    this.isAutoCommit = isAutoCommit;
  }

  /**
   * Enables or disables the filters.
   *
   * @param enableFilters true to enable filters
   */
  public void setEnableFilters(boolean enableFilters) {
    if (this.filtersEnabled == enableFilters) {
      return;
    }
    this.filtersEnabled = enableFilters;
    applyFilters();
  }

  /**
   * Replaces the item that matches the key of the given item, and fires a {@link StoreUpdateEvent} to indicate that
   * this change has occurred. Any changes to the previous model via it's record instance will be lost and the record
   * will be removed.
   *
   * This will not cause the sort or filter to be re-applied to the object.
   *
   * @param item the new item to take its place in the Store.
   */
  public abstract void update(M item);

  /**
   * Sets the filters to run again, whether they need it or not. Will fire a
   * Filter event if anything has changed.
   */
  protected abstract void applyFilters();

  /**
   * Creates a new master <code>Comparator</code> that runs all the
   * {@link StoreSortInfo} comparators in sequence, returning the value of the
   * first comparator that returns a non-zero value, or zero if no comparator
   * returns a non-zero value.
   *
   * @return the new master comparator
   */
  protected Comparator<M> buildFullComparator() {
    return new Comparator<M>() {
      public int compare(M o1, M o2) {
        for (int i = 0; i < comparators.size(); i++) {
          int val = comparators.get(i).compare(o1, o2);
          if (val != 0) {
            return val;
          }
        }
        return 0;
      }
    };
  }

  /**
   * Removes all items from the store.
   */
  protected void clear() {
    modifiedRecords.clear();
    records.clear();
  }

  /**
   * Ensures the store's handler manager exists, creating it if necessary (lazy
   * construction).
   *
   * @return the store's handler manager
   */
  protected HandlerManager ensureHandlers() {
    if (handlerManager == null) {
      handlerManager = new HandlerManager(this);
    }
    return handlerManager;
  }

  /**
   * Returns true if the store is sorted ({@link StoreSortInfo} has been added
   * and not cleared).
   *
   * @return true if the store is sorted.
   */
  protected boolean isSorted() {
    return comparators.size() != 0;
  }

  /**
   * Cleans up any reference the Store might've had to the model. Must be called
   * by subclasses. Returns a boolean, as {@link Collection#remove(Object)}
   *
   * @param model the data model to remove
   * @return boolean, indicating if it was removed from the Store. Subclasses
   *         should modify this to return false if necessary
   */
  protected boolean remove(M model) {
    String key = getKeyProvider().getKey(model);
    if (records.containsKey(key)) {
      modifiedRecords.remove(records.remove(key));
    }
    return true;
  }

  /**
   * Returns a ValueProvider that will read and write to the corresponding Record object if
   * necessary instead of the model itself. This allows sorting and filtering to cover the
   * new value in the record instead of the original value.
   * @param valueProvider an existing ValueProvider to wrap
   * @return a new ValueProvider of the same type, able to read and write from this store's records
   */
  public <V> ValueProvider<? super M, V> wrapRecordValueProvider(final ValueProvider<? super M, V> valueProvider) {
    if (isAutoCommit()) {
      return valueProvider;
    }
    return new ValueProvider<M, V>() {
      @Override
      public V getValue(M object) {
        if (hasRecord(object)) {
          return getRecord(object).getValue(valueProvider);
        }
        return valueProvider.getValue(object);
      }

      @Override
      public void setValue(M object, V value) {
        getRecord(object).addChange(valueProvider, value);
      }

      @Override
      public String getPath() {
        return valueProvider.getPath();
      }
    };
  }
}
TOP

Related Classes of com.sencha.gxt.data.shared.Store$StoreSortInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.