/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.properties;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import com.rapidminer.tools.AbstractObservable;
import com.rapidminer.tools.expression.FunctionDescription;
/**
* This class is the model for the {@link FunctionDecription}s in the
* {@link ExpressionPropertyDialog}
*
* @author Sabrina Kirstein
* @since 6.5.0
*/
public class FunctionDescriptionModel extends AbstractObservable<FunctionDescription> {
/** contains the list with ALL {@link FunctionDescription}s */
private Map<String, List<FunctionDescription>> modelMap = new LinkedHashMap<>();
/** contains the filtered list of {@link FunctionDescription}s */
private Map<String, List<FunctionDescription>> filteredModelMap = new LinkedHashMap<>();
/** the function name filter {@link String} */
private String filterNameString;
/** the function name filter {@link String} in lower case */
private String filterNameStringLowerCase;
/**
* comparator which is used to compare the importance of a {@link FunctionDescription} in regard
* to the {@link #filterNameString}. If the search string is contained in the name it's more
* important as if the search string is contained in the description.
*/
private Comparator<FunctionDescription> functionComparator = new Comparator<FunctionDescription>() {
@Override
public int compare(FunctionDescription o1, FunctionDescription o2) {
if (filterNameString.isEmpty()) {
// if there is no filtering, keep the initial sorting
return 0;
} else {
// if the search string is contained in the name it's more important as if
// the search string is contained in the description
if (o1.getDisplayName().toLowerCase(Locale.ENGLISH).contains(filterNameStringLowerCase)
&& !o2.getDisplayName().toLowerCase(Locale.ENGLISH).contains(filterNameStringLowerCase)) {
return -1;
} else if (!o1.getDisplayName().toLowerCase(Locale.ENGLISH).contains(filterNameStringLowerCase)
&& o2.getDisplayName().toLowerCase(Locale.ENGLISH).contains(filterNameStringLowerCase)) {
return +1;
} else {
return 0;
}
}
}
};
public FunctionDescriptionModel() {
clearContent();
}
/**
* Clears all model content.
*/
public void clearContent() {
modelMap.clear();
filteredModelMap.clear();
filterNameString = "";
filterNameStringLowerCase = "";
}
/**
* Add the given functions for the key (function group name). Applies filtering and sorting
* implicitly.
*
* @param key
* function group
* @param functions
* list of functions in the given function group
*/
public void addContent(List<FunctionDescription> functions) {
for (FunctionDescription function : functions) {
if (modelMap.containsKey(function.getGroupName())) {
modelMap.get(function.getGroupName()).add(function);
} else {
modelMap.put(function.getGroupName(), new LinkedList<FunctionDescription>());
modelMap.get(function.getGroupName()).add(function);
}
}
applyFilter();
applySorting();
}
/**
* Returns the filtered {@link Map} of {@link List}s of {@link FunctionDescription}s.
*
* @return
*/
public Map<String, List<FunctionDescription>> getFilteredModel() {
return filteredModelMap;
}
/**
* returns the filtered list of {@link FunctionDecription}s for one specific function group
* (defined by the group name)
*
* @param functionGroupName
* @return
*/
public List<FunctionDescription> getFilteredModel(String functionGroupName) {
return filteredModelMap.get(functionGroupName);
}
/**
* returns the filter name
*
* @return
*/
public String getFilterNameString() {
return filterNameString;
}
/**
* Filters the list of {@link FunctionDescription}s using the filterNameString.
*
* @param filterNameString
* search word which is used to filter functions
*/
public synchronized void setFilterNameString(String filterNameString) {
if (filterNameString == null) {
filterNameString = "";
}
// do nothing on equal filter name
if (filterNameString.equals(this.filterNameString)) {
return;
}
this.filterNameString = filterNameString;
this.filterNameStringLowerCase = filterNameString.toLowerCase(Locale.ENGLISH);
applyFilter();
applySorting();
fireUpdate();
}
/**
* Applies the current filter.
*/
private void applyFilter() {
filteredModelMap.clear();
for (Entry<String, List<FunctionDescription>> entry : modelMap.entrySet()) {
List<FunctionDescription> newList = new LinkedList<>();
for (FunctionDescription function : entry.getValue()) {
newList.add(function);
}
filteredModelMap.put(entry.getKey(), newList);
}
if (!getFilterNameString().isEmpty()) {
// apply filter on non empty string
for (Entry<String, List<FunctionDescription>> entry : filteredModelMap.entrySet()) {
// if the function group name already matches the search string, keep all group
// functions
if (entry.getKey().toLowerCase(Locale.ENGLISH).contains(filterNameStringLowerCase)) {
continue;
}
// remove the functions that do not fit the search string
for (int i = entry.getValue().size() - 1; i >= 0; i--) {
FunctionDescription function = entry.getValue().get(i);
if (!function.getDisplayName().toLowerCase(Locale.ENGLISH).contains(filterNameStringLowerCase)
&& !function.getHelpTextName().toLowerCase(Locale.ENGLISH).contains(filterNameStringLowerCase)
&& !function.getFunctionNameWithParameters().toLowerCase(Locale.ENGLISH)
.contains(filterNameStringLowerCase)
&& !function.getDescription().toLowerCase(Locale.ENGLISH).contains(filterNameStringLowerCase)) {
filteredModelMap.get(entry.getKey()).remove(i);
}
}
}
// if a function group has no fitting functions, remove the function group
for (String key : modelMap.keySet()) {
List<FunctionDescription> list = filteredModelMap.get(key);
if (list.isEmpty()) {
filteredModelMap.remove(key);
}
}
}
}
/**
* Sorts the {@link FunctionDescription} by the matching of the search string. If a function
* name contains the search string, it's more important as if the description contains the
* search string.
*/
private void applySorting() {
for (List<FunctionDescription> list : filteredModelMap.values()) {
Collections.sort(list, functionComparator);
}
}
}