package com.constellio.app.ui.framework.components.fields.autocomplete;
import static com.constellio.app.ui.i18n.i18n.$;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import com.constellio.app.ui.application.ConstellioUI;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.data.util.filter.SimpleStringFilter;
import com.vaadin.data.util.filter.UnsupportedFilterException;
import com.vaadin.shared.ui.combobox.FilteringMode;
import com.vaadin.ui.ComboBox;
/**
* Adapted from https://vaadin.com/forum#!/thread/897171/9060502
*
* @author Vincent
*
* @param <T>
* Type of the selected object
*/
public class BaseAutocompleteField<T> extends ComboBox {
public static final String STYLE_NAME = "autocomplete";
private static final String ERROR_STYLE_NAME = STYLE_NAME + "-error";
private static final String CAPTION_PROPERTY_ID = "caption";
private AutocompleteSuggestionsProvider<T> suggestionsProvider;
private Converter<String, T> itemConverter;
private int prefixSize = 0; // Default value
private AutocompleteContainer autocompleteContainer;
public BaseAutocompleteField(AutocompleteSuggestionsProvider<T> suggestionsProvider) {
super();
this.suggestionsProvider = suggestionsProvider;
addStyleName(STYLE_NAME);
setImmediate(true);
setFilteringMode(FilteringMode.STARTSWITH);
setInputPrompt($("BaseAutocompleteField.inputPrompt"));
setTextInputAllowed(true);
setNullSelectionAllowed(true);
setItemCaptionMode(ItemCaptionMode.PROPERTY);
setItemCaptionPropertyId(CAPTION_PROPERTY_ID);
autocompleteContainer = new AutocompleteContainer(prefixSize);
setContainerDataSource(autocompleteContainer);
setPageLength(suggestionsProvider.getBufferSize());
addValueChangeListener(new ValueChangeListener() {
@SuppressWarnings("unchecked")
@Override
public void valueChange(Property.ValueChangeEvent event) {
T newValue = (T) event.getProperty().getValue();
if (newValue != null && !autocompleteContainer.contains(newValue)) {
autocompleteContainer.addContainerFilter(null);
autocompleteContainer.addSuggestion(newValue);
}
}
});
}
@Override
public void setRequiredError(String requiredMessage) {
super.setRequiredError(requiredMessage);
addStyleName(ERROR_STYLE_NAME);
}
@Override
public void validate()
throws InvalidValueException {
try {
super.validate();
removeStyleName(ERROR_STYLE_NAME);
} catch (InvalidValueException e) {
throw e;
}
}
public AutocompleteSuggestionsProvider<T> getSuggestionsProvider() {
return suggestionsProvider;
}
public int getPrefixSize() {
return prefixSize;
}
public void setPrefixSize(int prefixSize) {
this.prefixSize = prefixSize;
}
public Converter<String, T> getItemConverter() {
return itemConverter;
}
public void setItemConverter(Converter<String, T> itemConverter) {
this.itemConverter = itemConverter;
}
private String getCaption(T object) {
String caption;
if (object != null) {
if (itemConverter != null) {
Locale locale = ConstellioUI.getCurrentSessionContext().getCurrentLocale();
caption = itemConverter.convertToPresentation(object, String.class, locale);
} else {
caption = object.toString();
}
} else {
caption = "";
}
return caption;
}
public interface AutocompleteSuggestionsProvider<T> extends Serializable {
List<T> suggest(String text);
int getBufferSize();
}
private class AutocompleteContainer extends IndexedContainer {
private final int subStringSize;
private String querySubString;
public AutocompleteContainer(int prefixSize) {
this.subStringSize = prefixSize;
addContainerProperty(CAPTION_PROPERTY_ID, String.class, null);
}
@SuppressWarnings("unchecked")
private boolean contains(T object) {
boolean inSuggestions = false;
for (Object itemId : getItemIds()) {
T suggestion = (T) itemId;
inSuggestions = object.equals(suggestion);
if (inSuggestions) {
break;
}
}
return inSuggestions;
}
@Override
protected boolean passesFilters(Object itemId) {
// The combobox only contains filtered data
return true;
}
public void addContainerFilter(Filter filter)
throws UnsupportedFilterException {
// Check to see if the filter is equal to null
if (filter == null) {
// remove the items
removeAllItems();
querySubString = null;
} else {
// remove the items
removeAllItems();
// check to see what type of filter we have
if (filter instanceof SimpleStringFilter) {
// get the query subString from the filter
String newFilterString = ((SimpleStringFilter) filter).getFilterString();
// check to make sure the substring isn't null and its not equal to the last one
if (newFilterString != null && !(newFilterString.equals(querySubString))) {
// set the sub string
querySubString = newFilterString;
// check the size on the sub string
if (querySubString.length() >= subStringSize) {
// get the results
querySuggestionsProvider(newFilterString);
// add the filter
super.addContainerFilter(filter);
} // end if the substring langth is long enough
}// end if newFilterString doesnt equal null and newFilterString doesnt equal last value
} // end if and instance of Simple Filter
} // end else
} // end addCoontainerFilter method
private void querySuggestionsProvider(String text) {
// Query the database here with the code you want
// Store it how ever you want this example uses a list to demonstrate how to get the data to display
List<T> dataList = suggestionsProvider.suggest(text);
// add the results to the container
// int i = 0;
Iterator<T> iterDataList = dataList.iterator();
while (iterDataList.hasNext()) {
T suggestion = iterDataList.next();
addSuggestion(suggestion);
// i++;
}// end while iter has next
}// end queryDataBase method
@SuppressWarnings("unchecked")
private void addSuggestion(T suggestion) {
String suggestionCaption = getCaption(suggestion);
Item item = addItem(suggestion);
item.getItemProperty(CAPTION_PROPERTY_ID).setValue(suggestionCaption);
}
} // end CustomLazyContainer class
}