Search code examples
gwtcelltablegwt-celltable

ListDataProvider.getList().remove doesn't use the KeyProvider


Calling the ListDataProvider.getList().remove(...) doesn't use the KeyProvider passed in constructor. Is this a GWT bug or is it supposed to work like this? I've attached the relevant unit test below.

Thank you,

    TestVO ri1 = new TestVO();
    ri1.setId(null);
    ri1.setName("msg1");
    TestVO ri2 = new TestVO();
    ri2.setId(null);
    ri2.setName("msg2");
    TestVO ri3 = new TestVO();
    ri3.setId(null);
    ri3.setName("msg2");

    ListDataProvider<TestVO> ldp = new ListDataProvider<>(new ProvidesKey<TestVO>() {
      @Override
      public Object getKey(TestVO pItem) {
        System.out.println("this never gets invoked");
        return pItem.getMessageType();
      }
    });
    ldp.setList(Lists.newArrayList(ri1, ri2));

    ldp.getList().remove(ri3);

    //this currently fails, actual size is 1 as it removes ri2
    assertEquals(2, ldp.getList().size());

Solution

  • No it's not a bug and no it doesn't use the getKey implementation you provided for finding the element in that list, because that method is used for other operations. Bear with me...

    ListDataProvider uses a ListWrapper which implements the List interface and ListWrapper is backed by an ordinary List that holds your objects. The ListWrapper has implementation for the methods you are using (add, set, remove etc) that manipulate the List that you passed Lists.newArrayList(ri1, ri2).

    This manipulation is accomplished by calling, among others, the standard operations of the List interface. So what is being called to determine which object should be removed is indexOf which in let's say ArrayList (that's what you are passing, so that's what is being used) uses (as you would expect) the equals method of TestVO which in case you haven't overriden is the one that you can find in the Object class.

    The getKey method that you provide is being used to identify the row inside a list of rows (inside the display you are using, like DataGrid). This can be found in AbstractDataProvider. This method, that calls your method, is called by other methods (see what I did there :P) all over the place in order to identify the row that needs some action. An example would be an update of a row inside a DataGrid. The method that will determine which row was updated is the method you provided. So if your implementation doesn't provide a unique key, you will have indication of an update for all the rows with the same key. So be careful.

      /**
       * Get the key for a list item. The default implementation returns the item
       * itself.
       *
       * @param item the list item
       * @return the key that represents the item
       */
      public Object getKey(T item) {
        return keyProvider == null ? item : keyProvider.getKey(item);
      }
    

    The implementation of indexOf in ArrayList

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
    

    This is the implemention of GWT

    /**
     * Construct a new {@link ListWrapper} that delegates flush calls to the
     * specified delegate.
     *
     * @param list the list to wrap
     * @param delegate the delegate
     * @param offset the offset of this list
     */
    private ListWrapper(List<T> list, ListWrapper delegate, int offset) {
      this.list = list;
      this.delegate = delegate;
      this.offset = offset;
    }
    
    @Override
    public void add(int index, T element) {
      try {
        list.add(index, element);
        minModified = Math.min(minModified, index);
        maxModified = size();
        modified = true;
        flush();
      } catch (IndexOutOfBoundsException e) {
        throw new IndexOutOfBoundsException(e.getMessage());
      }
    }
    @Override
    public T remove(int index) {
      try {
        T toRet = list.remove(index);
        minModified = Math.min(minModified, index);
        maxModified = size();
        modified = true;
        flush();
        return toRet;
      } catch (IndexOutOfBoundsException e) {
        throw new IndexOutOfBoundsException(e.getMessage());
      }
    }
    
    @Override
    public boolean remove(Object o) {
      int index = indexOf(o);
      if (index == -1) {
        return false;
      }
      remove(index);
      return true;
    }
    
    public void setList(List<T> listToWrap) {
      listWrapper = new ListWrapper(listToWrap);
      listWrapper.minModified = 0;
      listWrapper.maxModified = listWrapper.size();
      listWrapper.modified = true;
      flush();
    }
    
    public List<T> getList() {
      return listWrapper;
    }