/** * 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.Iterator; 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.example.Attribute; import com.rapidminer.tools.AbstractObservable; import com.rapidminer.tools.Ontology; import com.rapidminer.tools.expression.ExampleResolver; import com.rapidminer.tools.expression.FunctionDescription; import com.rapidminer.tools.expression.FunctionInput; import com.rapidminer.tools.expression.FunctionInput.Category; /** * This class is the model for the actual {@link Attribute}s, macros and constants in the * {@link ExpressionPropertyDialog}. * * @author Sabrina Kirstein * */ public class FunctionInputsModel extends AbstractObservable<FunctionInputPanel> { /** contains the list with ALL inputs */ private LinkedHashMap<String, List<FunctionInput>> modelMap = new LinkedHashMap<>(); /** contains the filtered list of inputs */ private LinkedHashMap<String, List<FunctionInput>> filteredModelMap = new LinkedHashMap<>(); /** the function name filter {@link String} */ private String filterNameString; private boolean nominalFilter = false; private boolean numericFilter = false; private boolean dateTimeFilter = false; /** * Sorts the incoming {@link FunctionInput}s by category first */ private static final Comparator<FunctionInput> FUNCTION_INPUT_COMPARATOR = new Comparator<FunctionInput>() { @Override public int compare(FunctionInput o1, FunctionInput o2) { if (o1 == null && o2 == null) { return 0; } else if (o1 != null && o2 == null) { return 1; } else if (o1 == null && o2 != null) { return -1; } else { if (o1.getCategory() == Category.DYNAMIC && o2.getCategory() == Category.DYNAMIC) { if (o1.getCategoryName().equals(o2.getCategoryName())) { return o1.getName().compareToIgnoreCase(o2.getName()); } else if (o1.getCategoryName().equals(ExampleResolver.KEY_ATTRIBUTES)) { return -1; } else if (o2.getCategoryName().equals(ExampleResolver.KEY_ATTRIBUTES)) { return 1; } else if (o1.getCategoryName().equals(ExampleResolver.KEY_SPECIAL_ATTRIBUTES)) { return -1; } else if (o1.getCategoryName().equals(ExampleResolver.KEY_SPECIAL_ATTRIBUTES)) { return 1; } else { return 0; } } else if (o1.getCategory() == Category.SCOPE && o2.getCategory() == Category.SCOPE) { return o1.useCustomIcon() ? -1 : 1; } else if (o1.getCategory().ordinal() < o2.getCategory().ordinal()) { return 1; } else if (o2.getCategory().ordinal() < o1.getCategory().ordinal()) { return -1; } else { return 0; } } } }; /** * Creates a model for the possible {@link FunctionDescription} inputs */ public FunctionInputsModel() { clearContent(); } /** * Clears all model content. */ public void clearContent() { modelMap = new LinkedHashMap<>(); filteredModelMap = new LinkedHashMap<>(); filterNameString = ""; nominalFilter = false; numericFilter = false; dateTimeFilter = false; } /** * Add the given inputs for the key (type of input) * * @param key * @param inputs */ public void addContent(List<FunctionInput> inputs) { Collections.sort(inputs, FUNCTION_INPUT_COMPARATOR); for (FunctionInput input : inputs) { if (input.isVisible()) { if (modelMap.containsKey(input.getCategoryName())) { modelMap.get(input.getCategoryName()).add(input); } else { modelMap.put(input.getCategoryName(), new LinkedList<FunctionInput>()); modelMap.get(input.getCategoryName()).add(input); } } } applyFilter(); } /** * Returns the filtered {@link Map} of {@link List}s of Strings. * * @return */ public Map<String, List<FunctionInput>> getFilteredModel() { return filteredModelMap; } /** * returns the filtered map of Strings for one specific input type (defined by the type name) * * @param type * @return */ public List<FunctionInput> getFilteredModel(String type) { return filteredModelMap.get(type); } /** * returns the filter name * * @return */ public String getFilterNameString() { return filterNameString; } /** * Filters the list of inputs using the filterNameString. * * @param filterNameString */ public synchronized void setFilterNameString(String filterNameString) { // do nothing on equal filter name if (filterNameString.equals(this.filterNameString)) { return; } this.filterNameString = filterNameString; applyFilter(); fireUpdate(); } /** * set the filter to show the nominal attributes and constants * * @param filterToggled */ public void setNominalFilter(boolean filterToggled) { nominalFilter = filterToggled; applyFilter(); fireUpdate(); } /** * get the nominal filter state * * @return if the nominal filter is toggled */ public boolean isNominalFilterToggled() { return nominalFilter; } /** * set the filter to show the numeric attributes and constants * * @param filterToggled */ public void setNumericFilter(boolean filterToggled) { numericFilter = filterToggled; applyFilter(); fireUpdate(); } /** * get the numeric filter state * * @return if the numeric filter is toggled */ public boolean isNumericFilterToggled() { return numericFilter; } /** * set the filter to show the date time attributes and constants * * @param filterToggled */ public void setDateTimeFilter(boolean filterToggled) { dateTimeFilter = filterToggled; applyFilter(); fireUpdate(); } /** * get the date time filter state * * @return if the date time filter is toggled */ public boolean isDateTimeFilterToggled() { return dateTimeFilter; } /** * Applies the current filters. */ private synchronized void applyFilter() { filteredModelMap = new LinkedHashMap<>(); for (Entry<String, List<FunctionInput>> entry : modelMap.entrySet()) { List<FunctionInput> list = entry.getValue(); List<FunctionInput> newList = new LinkedList<>(); for (FunctionInput inputValue : list) { newList.add(inputValue); } filteredModelMap.put(entry.getKey(), newList); } boolean anyFilterToggled = isNominalFilterToggled() || isNumericFilterToggled() || isDateTimeFilterToggled(); // apply filter on non empty string for (String key : filteredModelMap.keySet()) { List<FunctionInput> list = filteredModelMap.get(key); // if the function group name already matches the search string, keep all group // inputs Iterator<FunctionInput> entryIterator = list.iterator(); // remove the inputs that do not fit the search string while (entryIterator.hasNext()) { boolean alreadyRemoved = false; FunctionInput entry = entryIterator.next(); String entryName = entry.getName(); // check whether the input entry with the given type if (anyFilterToggled) { // should be shown if (!showInputEntry(entry.getType())) { entryIterator.remove(); alreadyRemoved = true; } } if (!getFilterNameString().isEmpty()) { if (!entryName.toLowerCase(Locale.ENGLISH).contains(filterNameString.toLowerCase(Locale.ENGLISH)) && !alreadyRemoved) { if (key.toLowerCase(Locale.ENGLISH).contains(filterNameString.toLowerCase(Locale.ENGLISH))) { continue; } entryIterator.remove(); } } } } // if a function group has no fitting inputs, remove the input group for (String key : modelMap.keySet()) { List<FunctionInput> list = filteredModelMap.get(key); if (list.isEmpty()) { filteredModelMap.remove(key); } } } /** * Checks and returns whether an input entry is shown based on the entryType and the toggled * filters * * @param entryType * @param anyFilterToggled * @return if the entry should be shown */ private boolean showInputEntry(int entryType) { switch (entryType) { case Ontology.INTEGER: case Ontology.REAL: case Ontology.NUMERICAL: if (!isNumericFilterToggled()) { return false; } else { return true; } case Ontology.DATE_TIME: case Ontology.DATE: case Ontology.TIME: if (!isDateTimeFilterToggled()) { return false; } else { return true; } case Ontology.FILE_PATH: case Ontology.STRING: case Ontology.POLYNOMINAL: case Ontology.BINOMINAL: case Ontology.NOMINAL: if (!isNominalFilterToggled()) { return false; } else { return true; } default: return false; } } }