org.wicketstuff.datatable_autocomplete.provider.TrieDataProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wicketstuff-datatable-autocomplete Show documentation
Show all versions of wicketstuff-datatable-autocomplete Show documentation
The Datatable autocomplete project provides a Trie datastructure that allows AJAX searches on large datasets fast.
It is not memory efficient but it is fast especially to know how many results a given prefix matches.
Provides a Datatable component when tied to a textfield will show the matched objects in a table format.
/*
*
* ==============================================================================
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.wicketstuff.datatable_autocomplete.provider;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.model.IModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wicketstuff.datatable_autocomplete.provider.utils.DataProviderUtils;
import org.wicketstuff.datatable_autocomplete.trie.ITrieFilter;
import org.wicketstuff.datatable_autocomplete.trie.Trie;
/**
* @author mocleiri
*
* A SortableDataProvider connector which loads the data from the backing Trie.
*
*/
public class TrieDataProvider extends SortableDataProvider
{
private static final Logger log = LoggerFactory.getLogger(TrieDataProvider.class);
private static final long serialVersionUID = 6404076914195910834L;
// prevents the underlying Trie from being serialized with the provider.
// it is a big performance problem if the Trie is serialized (especially in
// AJAX cases).
protected final ITrieProvider trieProvider;
private List currentListData = null;
private final IModel fieldStringModel;
private final IProviderSorter sorter;
private ITrieFilter trieResultFilter;
// default to show no results for an empty string
private boolean noResultsOnEmptyString = true;
private boolean matchAnyWhereInString = false;
private final IModelProvider modelProvider;
private Map resultLimitMap = new LinkedHashMap();
protected IModel getInputModel()
{
return fieldStringModel;
}
/**
* @param noResultsOnEmptyString
* the noResultsOnEmptyString to set
*/
public void setNoResultsOnEmptyString(boolean noResultsOnEmptyString)
{
this.noResultsOnEmptyString = noResultsOnEmptyString;
}
/**
* @param matchAnyWhereInString
* the matchAnyWhereInString to set
*/
public void setMatchAnyWhereInString(boolean matchAnyWhereInString)
{
this.matchAnyWhereInString = matchAnyWhereInString;
}
/**
* @return the matchAnyWhereInString
*/
public boolean isMatchAnyWhereInString()
{
return matchAnyWhereInString;
}
/*
* (non-Javadoc)
*
* @see org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider #detach()
*/
@Override
public void detach()
{
/*
* If we want to support selectable values then we need to make the list last longer than
* just one rendered pass.
*/
clear();
super.detach();
}
public final void clear()
{
if (currentListData != null)
{
currentListData.clear();
currentListData = null;
}
}
/**
* @param fieldStringModel
*
*/
public TrieDataProvider(ITrieProvider trieProvider, ITrieFilter resultFilter,
IModel fieldStringModel, IProviderSorter sorter, IModelProvider modelProvider)
{
super();
this.trieProvider = trieProvider;
trieResultFilter = resultFilter;
this.fieldStringModel = fieldStringModel;
this.sorter = sorter;
this.modelProvider = modelProvider;
}
public int size()
{
String prefix = fieldStringModel.getObject();
if (prefix == null)
{
return 0;
}
else if (noResultsOnEmptyString && prefix.length() == 0)
{
// show no results since no input filter specified.
return 0;
}
else
{
// if no input is given any element in the entire trie is
// selectable.
if (currentListData == null)
{
Trie trie = trieProvider.provideTrie();
if (trie == null)
{
log.warn("trie is unexpectantly null!");
currentListData = new LinkedList();
return 0;
}
// prefix matching
Integer limit = this.resultLimitMap.get(prefix.length());
if (limit == null)
{
// no limit
currentListData = trie.getWordList(prefix, this.trieResultFilter);
}
else
{
currentListData = trie.getWordList(prefix, this.trieResultFilter, limit);
}
}
return currentListData.size();
}
}
/*
* (non-Javadoc)
*
* @see org.apache.wicket.markup.repeater.data.IDataProvider#iterator(int, int)
*/
public Iterator iterator(int first, int count)
{
String prefix = fieldStringModel.getObject();
if (prefix == null || currentListData == null)
return new LinkedList().iterator();
SortParam sort = super.getSort();
if (sort != null && this.currentListData.size() > 1)
{
Comparator c = sorter.getComparatorForProperty(sort);
if (c != null)
Collections.sort(this.currentListData, c);
}
int size = currentListData.size();
int last = DataProviderUtils.getLastIndex(size, first, count);
if (first > last)
{
log.warn("indexing problem");
last = DataProviderUtils.getLastIndex(size, first, count);
}
return currentListData.subList(first, last).iterator();
}
/*
* (non-Javadoc)
*
* @see org.apache.wicket.markup.repeater.data.IDataProvider#model(java.lang. Object)
*/
public IModel model(C object)
{
return modelProvider.model(object);
}
/**
* Used to set the upper bounds on results returned based on the length of the prefix being
* searched.
*
* @param resultMatchMap
*
*/
public void setMaxResultMap(Map resultMatchMap)
{
this.resultLimitMap = resultMatchMap;
}
}