
org.vaadin.viritin.fields.LazyComboBox Maven / Gradle / Ivy
package org.vaadin.viritin.fields;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.ObjectUtils;
import org.vaadin.viritin.LazyList;
import org.vaadin.viritin.ListContainer;
import org.vaadin.viritin.util.HtmlElementPropertySetter;
import com.vaadin.data.Container;
import com.vaadin.data.Container.Filterable;
import com.vaadin.data.util.filter.UnsupportedFilterException;
import com.vaadin.server.Resource;
import com.vaadin.shared.Version;
import com.vaadin.shared.ui.combobox.FilteringMode;
import com.vaadin.ui.AbstractSelect.ItemCaptionMode;
import com.vaadin.ui.ComboBox;
import java.util.Map;
/**
* This class tries to provide a simple lazy loading connection form ComboBox to
* typical service layers. See Viritin projects tests for example usage.
*
* @author Matti Tahvonen
* @param the type of entities listed in the combobox
*/
public class LazyComboBox extends TypedSelect {
private static final long serialVersionUID = 2332969066755466769L;
private String currentFilter;
private FilterablePagingProvider fpp;
private FilterableCountProvider fcp;
private String lastRawFilter;
private boolean useRawFilter = false;
/**
* Interface via the LazyComboBox communicates with the "backend"
*
* @param The type of the objects in the list
*/
public interface FilterablePagingProvider {
/**
* Fetches one "page" of entities form the backend. The amount
* "maxResults" should match with the value configured for the LazyList
*
* @param firstRow the index of first row that should be fetched
* @param filter the filter typed in by the user
* @return a sub list from given first index
*/
public List findEntities(int firstRow, String filter);
}
public interface FilterableCountProvider {
public int size(String filter);
}
private LazyList piggybackLazyList;
/* Instantiates a memory and CPU efficient ComboBox, typically wired to EJB
* or Spring Data repository. By default page size of
* LazyList.DEFAULT_PAGE_SIZE (30) is used.
* If you use this constructor, be sure to call loadFrom method as well
* to define how options should be loaded from the backend.
*
* @param elementType the type of options in the select
*/
public LazyComboBox(Class aClass) {
this(aClass, new FilterablePagingProvider() {
@Override
public List findEntities(int firstRow, String filter) {
return Collections.emptyList();
}
}, new FilterableCountProvider() {
@Override
public int size(String filter) {
return 0;
}
});
}
/**
* Instantiates a memory and CPU efficient ComboBox, typically wired to EJB
* or Spring Data repository. By default page size of
* LazyList.DEFAULT_PAGE_SIZE (30) is used.
*
* @param elementType the type of options in the select
* @param filterablePageProvider the interface via entities are fetched
* @param countProvider the interface via the count of items is detected
*/
public LazyComboBox(Class elementType,
final FilterablePagingProvider filterablePageProvider,
final FilterableCountProvider countProvider) {
this(elementType, filterablePageProvider, countProvider,
LazyList.DEFAULT_PAGE_SIZE);
}
/**
* Instantiates a memory and CPU efficient ComboBox, typically wired to EJB
* or Spring Data repository.
*
* @param elementType the type of options in the select
* @param filterablePageProvider the interface via entities are fetched
* @param countProvider the interface via the count of items is detected
* @param pageLength the maximum page size to be used with service calls
*/
public LazyComboBox(Class elementType,
final FilterablePagingProvider filterablePageProvider,
final FilterableCountProvider countProvider, int pageLength) {
this();
initList(elementType, filterablePageProvider, countProvider, pageLength);
}
protected final ComboBox initList(
Class elementType,
FilterablePagingProvider filterablePageProvider,
FilterableCountProvider countProvider1, int pageLength) {
this.fpp = filterablePageProvider;
this.fcp = countProvider1;
// piggyback to simple paging provider
piggybackLazyList = new LazyList<>(new LazyList.PagingProvider() {
private static final long serialVersionUID = 1027614132444478021L;
@Override
public List findEntities(int firstRow) {
return fpp.findEntities(firstRow,
getCurrentFilter());
}
},
new LazyList.CountProvider() {
private static final long serialVersionUID = -7339189124024626177L;
@Override
public int size() {
return fcp.size(getCurrentFilter());
}
}, pageLength);
final ComboBox comboBox = new ComboBox() {
private static final long serialVersionUID = -4543249010409767251L;
@SuppressWarnings("unchecked")
@Override
public String getItemCaption(Object itemId) {
return LazyComboBox.this.getCaption((T) itemId);
}
@Override
public Resource getItemIcon(Object itemId) {
if (getIconGenerator() != null) {
return LazyComboBox.this.getIcon((T) itemId);
}
return super.getItemIcon(itemId);
}
@Override
protected Container.Filter buildFilter(String filterString,
FilteringMode filteringMode) {
if (getValue() != null && getItemCaption(getValue()).equals(
filterString)) {
// Yes, the combobox calls filtering with the item caption when opening popup with selection,
// ignore that, fall back to ""
filterString = "";
}
/*
* Save for the thinner interface.
*/
if (ObjectUtils.notEqual(currentFilter, filterString)) {
currentFilter = filterString;
piggybackLazyList.reset();
}
return super.buildFilter(filterString, filteringMode);
}
{
// This is needed for the lazy loading to work for some reason
setItemCaptionMode(ItemCaptionMode.PROPERTY);
setItemCaptionPropertyId("FAKE");
}
@Override
public void changeVariables(Object source, Map variables) {
String newFilter = (String) variables.get("filter");
if (newFilter != null) {
lastRawFilter = newFilter;
}
super.changeVariables(source, variables);
}
};
setBic(new DummyFilterableListContainer<>(elementType,
piggybackLazyList));
comboBox.setContainerDataSource(getBic());
if (Version.getMajorVersion() >= 7 && Version.getMinorVersion() >= 5) {
// broken in earler Vaadin versions, so skip otherwise
// Set to false for much better performance if selection is in
// large index
comboBox.setScrollToSelectedItem(false);
}
fixComboBoxVaadinIssue16647(comboBox);
setSelectInstance(comboBox);
return comboBox;
}
/**
* Set a new strategies how to load options.
*
* @param filterablePagingProvider the paging provider that gives the actual
* options in pages
* @param filterableCountProvider the count provider to give the total about
* of options with current filter
*/
public void loadFrom(FilterablePagingProvider filterablePagingProvider, FilterableCountProvider filterableCountProvider) {
this.fpp = filterablePagingProvider;
this.fcp = filterableCountProvider;
refresh();
}
/**
* Set a new strategies how to load options.
*
* @param filterablePagingProvider the paging provider that gives the actual
* options in pages
* @param filterableCountProvider the count provider to give the total about
* of options with current filter
* @param pageLength the length of the pages that component should use to
* access providers
*/
public void loadFrom(FilterablePagingProvider filterablePagingProvider, FilterableCountProvider filterableCountProvider, int pageLength) {
this.fpp = filterablePagingProvider;
this.fcp = filterableCountProvider;
// Need to re-create the piggybackList & set container, some refactoring should be done here
piggybackLazyList = new LazyList<>(new LazyList.PagingProvider() {
private static final long serialVersionUID = 1027614132444478021L;
@Override
public List findEntities(int firstRow) {
return fpp.findEntities(firstRow,
getCurrentFilter());
}
},
new LazyList.CountProvider() {
private static final long serialVersionUID = -7339189124024626177L;
@Override
public int size() {
return fcp.size(getCurrentFilter());
}
}, pageLength);
setBic(new DummyFilterableListContainer(getType(),
piggybackLazyList));
getSelect().setContainerDataSource(getBic());
}
public static void fixComboBoxVaadinIssue16647(final ComboBox comboBox) {
HtmlElementPropertySetter heps = new HtmlElementPropertySetter(comboBox);
heps.setProperty("./input", "autocorrect", "off");
heps.setProperty("./input", "autocomplete", "off");
heps.setProperty("./input", "autocapitalize", "off");
}
/**
* Instantiates a new LazyCombobox. Be sure to call preparePiggybackLazyList
*
*/
protected LazyComboBox() {
setCaptionGenerator(new CaptionGenerator() {
private static final long serialVersionUID = 9213991656985157568L;
@Override
public String getCaption(T option) {
return option.toString();
}
});
}
/**
* Refreshes entities cached in the lazy backing list.
*/
public void refresh() {
piggybackLazyList.reset();
markAsDirty();
}
public String getCurrentFilter() {
return useRawFilter ? lastRawFilter : currentFilter;
}
public String getRawFilter() {
return lastRawFilter;
}
public boolean isUseRawFilter() {
return useRawFilter;
}
/**
* Using this method LazyComboBox can be made to use "raw" non lowercased
* filters string.
*
* @param useRawFilter true if raw filter string should be used.
*/
public void setUseRawFilter(boolean useRawFilter) {
this.useRawFilter = useRawFilter;
}
/**
* Just fake for ComboBox that we implement Filterable, handle filtering
* with simpler interfaces.
*
* @param the type of the elements in the list
*/
private static class DummyFilterableListContainer extends ListContainer
implements Filterable {
private static final long serialVersionUID = -1165474591390168493L;
DummyFilterableListContainer(Class type,
Collection backingList) {
super(type, backingList);
}
@Override
public void addContainerFilter(Filter filter) throws UnsupportedFilterException {
// Who cares
}
@Override
public void removeContainerFilter(Filter filter) {
/// So not
}
@Override
public void removeAllContainerFilters() {
// Why would I bother
}
@Override
public Collection getContainerFilters() {
// Why?
return null;
}
@Override
public boolean containsId(Object itemId) {
// Vaadin is pretty eager to do "sanity checks", which is not good
// for performance. This may cause a full DB seek, so just fake
// that all values are there.
return true;
}
}
@Override
public LazyComboBox withWidth(String width) {
return (LazyComboBox) super.withWidth(width);
}
@Override
public LazyComboBox withWidth(float width, Unit unit) {
return (LazyComboBox) super.withWidth(width, unit);
}
@Override
public LazyComboBox withReadOnly(boolean readOnly) {
return (LazyComboBox) super.withReadOnly(readOnly);
}
@Override
public LazyComboBox withFullWidth() {
return (LazyComboBox) super.withFullWidth();
}
@Override
public LazyComboBox setNullSelectionAllowed(boolean nullAllowed) {
return (LazyComboBox) super.setNullSelectionAllowed(nullAllowed);
}
@Override
public LazyComboBox setCaptionGenerator(
CaptionGenerator captionGenerator) {
return (LazyComboBox) super.setCaptionGenerator(captionGenerator);
}
@Override
public LazyComboBox setIconGenerator(IconGenerator generator) {
return (LazyComboBox) super.setIconGenerator(generator);
}
@Override
public LazyComboBox addMValueChangeListener(
MValueChangeListener listener) {
return (LazyComboBox) super.addMValueChangeListener(listener);
}
@Override
public LazyComboBox setFieldType(Class type) {
return (LazyComboBox) super.setFieldType(type);
}
@Override
public LazyComboBox withCaption(String caption) {
return (LazyComboBox) super.withCaption(caption);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy