/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.xpn.xwiki.gwt.api.client.widgets;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Set;
/**
* SuggestOracle to suggest a list of words with respect to a given list of separators.
*
* The <tt>SuggestOracle</tt> used for a single word suggestion is set through
* {@link com.xpn.xwiki.gwt.api.client.widgets.WordListSuggestOracle#setWordOracle(com.google.gwt.user.client.ui.SuggestOracle)}
* and the separators through {@link com.xpn.xwiki.gwt.api.client.widgets.WordListSuggestOracle#setSeparators(String)}.
* Whether this oracle generates unique suggestions with respect to the already typed list, can
* be configured through {@link WordListSuggestOracle#setSuggestUnique(boolean)}.
*/
public class WordListSuggestOracle extends SuggestOracle {
/**
* oracle to use for suggesting a single word
*/
protected SuggestOracle wordOracle;
/**
* multiple words separator
*/
protected String separators;
/**
* whether this oracle omits words that already are in the typed list or not
*/
protected boolean suggestUnique;
/**
* Default constructor: builds an oracle with an empty {@link com.google.gwt.user.client.ui.SuggestOracle}
* and the space as a separator.
* By default, this oracle generates suggestions that duplicate existing words in the list.
*/
public WordListSuggestOracle() {
this(new MultiWordSuggestOracle(), " ", false);
}
/**
* Builds an oracle with the specified {@link com.google.gwt.user.client.ui.SuggestOracle} and
* the space as a default separator. By default, this oracle generates suggestions that
* duplicate existing words in the list.
*
* @param wordOracle the oracle to be used for predicting a single word.
*/
public WordListSuggestOracle(SuggestOracle wordOracle) {
this(wordOracle, " ", false);
}
/**
* Builds an wordOracle with the specified {@link com.google.gwt.user.client.ui.SuggestOracle}
* and separators. By default, this oracle generates suggestions that duplicate existing words
* in the list.
*
* @param wordOracle the wordOracle to be used for predicting a single word
* @param separators the separators between the words in the word list
*/
public WordListSuggestOracle(SuggestOracle wordOracle, String separators) {
this(wordOracle, separators, false);
}
/**
* Builds an wordOracle with the specified {@link com.google.gwt.user.client.ui.SuggestOracle},
* separators and specifying weather it suggests words that are already in the list or not.
*
* @param wordOracle the wordOracle to be used for predicting a single word
* @param separators the separators between the words in the word list
*/
public WordListSuggestOracle(SuggestOracle wordOracle, String separators,
boolean suggestUnique) {
this.setWordOracle(wordOracle);
this.setSeparators(separators);
this.setSuggestUnique(suggestUnique);
}
/**
* Returns a list of suggestions for the last word in the list.
*
* @param request
* @param callback
*/
@Override
public void requestSuggestions(Request request, final Callback callback) {
//get query
String query = request.getQuery();
//extract the last part of it
int lastSeparator = -1;
for (int i = 0; i < this.separators.length(); i++) {
int currentSeparator = query.lastIndexOf(this.separators.charAt(i));
if (currentSeparator > lastSeparator) {
lastSeparator = currentSeparator;
}
}
String oneWordQuery = query.substring(lastSeparator + 1);
final String prefix = query.substring(0, lastSeparator + 1);
// If this oracle is set to suggest unique, parse the rest of the list and get the
// existing words
final Set existingWords;
// Do the parsing only if needed. It might be too time consuming
if (this.suggestUnique) {
existingWords = this.parseQueryString(prefix);
} else {
existingWords = new HashSet();
}
// Get the response from the embedded wordOracle
request.setQuery(oneWordQuery);
Callback innerCallback = new Callback() {
@Override
public void onSuggestionsReady(Request defaultRequest, Response defaultResponse) {
Response wordListResponse = new Response();
ArrayList suggestions = new ArrayList();
// Generate the word list response from the single response one
for (Iterator sIt = defaultResponse.getSuggestions().iterator(); sIt.hasNext();) {
Suggestion oldSuggestion = (Suggestion) sIt.next();
String oneWordSuggestion = oldSuggestion.getReplacementString();
// If we suggest unique and suggestion already exists, skip it
if (suggestUnique && existingWords.contains(oneWordSuggestion)) {
continue;
}
// Display one, replace all
MultiWordSuggestOracle.MultiWordSuggestion newSuggestion =
new MultiWordSuggestOracle.MultiWordSuggestion(
prefix + oneWordSuggestion, oldSuggestion.getDisplayString());
suggestions.add(newSuggestion);
}
wordListResponse.setSuggestions(suggestions);
callback.onSuggestionsReady(defaultRequest, wordListResponse);
}
};
this.wordOracle.requestSuggestions(request, innerCallback);
}
/**
* Parses the passed query string into a list of tokens, based on the instance
* <tt>separators</tt>.
*
* @param queryString the string to parse
* @return the list of words, excluding empty words and duplicates
*/
protected Set parseQueryString(String queryString) {
Set existingWords = new HashSet();
int lastSeparator = queryString.length();
if (this.suggestUnique) {
while (lastSeparator > 0) {
int currentLastSeparator = -1;
// Find the last occurrence of any separator
for (int i = 0; i < this.separators.length(); i++) {
int currentSeparator =
queryString.substring(0, lastSeparator).lastIndexOf(
this.separators.charAt(i));
if (currentSeparator > currentLastSeparator) {
currentLastSeparator = currentSeparator;
}
}
// Get the current word determined by the last separator
String currentWord =
queryString.substring(currentLastSeparator + 1, lastSeparator);
if (currentWord.length() > 0) {
existingWords.add(currentWord);
}
lastSeparator = currentLastSeparator;
}
}
return existingWords;
}
public String getSeparators() {
return separators;
}
public void setSeparators(String separators) {
this.separators = separators;
}
@Override
public boolean isDisplayStringHTML() {
return this.wordOracle.isDisplayStringHTML();
}
public SuggestOracle getWordOracle() {
return wordOracle;
}
public void setWordOracle(SuggestOracle wordOracle) {
this.wordOracle = wordOracle;
}
public boolean getSuggestUnique() {
return suggestUnique;
}
public void setSuggestUnique(boolean suggestUnique) {
this.suggestUnique = suggestUnique;
}
}