/**
* 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.io.IOException;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import com.rapidminer.gui.properties.SettingsItem.Type;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.ParameterService;
import com.rapidminer.tools.Tools;
/**
* This singleton instance handles {SettingItem}s. It contains a map, which maps settings keys to
* {@link SettingsItem}s. Additionally to some default map methods and helper methods are provided.
*
* @author Adrian Wilke
*/
public enum SettingsItems {
INSTANCE;
/** Maps keys to SettingsItem objects */
private Map<String, SettingsItem> itemMap = new LinkedHashMap<>();
/** Was XML of RapidMiner Studio parsed successfully? */
private boolean studioXmlParsed = false;
/** Singleton: Empty private constructor */
private SettingsItems() {}
/** Removes empty subgroups and groups */
public void clean() {
List<SettingsItem> mapItems = getItems(Type.SUB_GROUP);
Iterator<SettingsItem> mapIterator = mapItems.iterator();
while (mapIterator.hasNext()) {
SettingsItem item = mapIterator.next();
if (item.getChildren().isEmpty()) {
try {
remove(item.getKey());
} catch (RuntimeException runtimeException) {
// This would be a failure in the settings structure and should not happen.
// The log is to notice an improbable occurrence.
LogService.getRoot().log(Level.WARNING, "com.rapidminer.gui.properties.SettingsItems.remove_item_error",
runtimeException);
}
}
}
mapItems = getItems(Type.GROUP);
mapIterator = mapItems.iterator();
while (mapIterator.hasNext()) {
SettingsItem item = mapIterator.next();
if (item.getChildren().isEmpty()) {
try {
remove(item.getKey());
} catch (RuntimeException runtimeException) {
// This would be a failure in the settings structure and should not happen.
// The log is to notice an improbable occurrence.
LogService.getRoot().log(Level.WARNING, "com.rapidminer.gui.properties.SettingsItems.remove_item_error",
runtimeException);
}
}
}
};
/** Checks if a key is known. */
public boolean containsKey(String key) {
return itemMap.containsKey(key);
}
/** Returns item or <code>null</code>, if key is unknown. */
public SettingsItem get(String key) {
return itemMap.get(key);
}
/** Returns all items of the specified type. */
public List<SettingsItem> getItems(Type type) {
List<SettingsItem> settingsItems = new LinkedList<>();
for (SettingsItem settingsItem : itemMap.values()) {
if (settingsItem.getType().equals(type)) {
settingsItems.add(settingsItem);
}
}
return settingsItems;
}
/** Returns all known keys. */
public Collection<String> getKeys() {
return new HashSet<String>(itemMap.keySet());
}
/**
* Parses XML file of RapidMiner Studio with settings structure.
*
* Sets {@link SettingsItems#studioXmlParsed} to the return value.
*
* @return <code>true</code>, if the XML was parsed successfully. <code>false</code>, if not.
*/
public boolean parseStudioXml() {
try {
itemMap = new SettingsXmlHandler().parse(Tools.getResource(SettingsXmlHandler.SETTINGS_XML_FILE).toURI());
studioXmlParsed = true;
return true;
} catch (ParserConfigurationException | SAXException | IOException | URISyntaxException e) {
String[] params = new String[2];
params[0] = SettingsXmlHandler.SETTINGS_XML_FILE;
params[1] = e.getMessage();
LogService.getRoot().log(Level.WARNING, "com.rapidminer.gui.properties.SettingsItems.parse_xml_error", params);
studioXmlParsed = false;
return false;
// Must not throw an exception, as the settings work without the structure inside XML.
}
}
/**
* Adds an item to the map of used SettingsItem objects. Checks for <code>null</code> and empty
* values of the parameters.
*/
public void put(String key, SettingsItem item) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Key not set");
}
if (item == null) {
throw new IllegalArgumentException("No item for key " + key);
}
itemMap.put(key, item);
}
/**
* Removes item from {@link SettingsItems} and the {@link SettingsItem} internal hierarchy.
*
* @param key
* Key of item
* @throws IllegalStateException
* If key is not known or related item has children
*/
public void remove(String key) throws IllegalStateException {
remove(key, true, true);
}
/**
* Removes item from {@link SettingsItems} and/or the {@link SettingsItem} internal hierarchy.
* The execution of the remove operation can be set for each of the both data structures.
*
* @param key
* Key of item
* @param removeFromSettingsItems
* Remove from {@link SettingsItems} data structure
* @param removeFromHierarchy
* Removes from {@link SettingsItem} internal hierarchy
*
* @throws IllegalStateException
* If key is not known or related item has children
*/
public void remove(String key, boolean removeFromSettingsItems, boolean removeFromHierarchy)
throws IllegalStateException {
if (!itemMap.containsKey(key)) {
throw new IllegalStateException("Could not remove settings item " + key + " as it is not known");
}
if (removeFromHierarchy) {
SettingsItem item = itemMap.get(key);
if (item.getChildren().isEmpty()) {
if (item.getParent() != null) {
item.getParent().getChildren().remove(item);
}
} else {
throw new IllegalStateException("Could not remove settings item " + key + " as it has children");
}
}
if (removeFromSettingsItems) {
itemMap.remove(key);
}
}
/** Removes all elements of type parameter, whose keys are not contained in keepKeys */
public void removeParameterInverse(Collection<String> keepKeys) {
Iterator<Entry<String, SettingsItem>> itemMapIterator = itemMap.entrySet().iterator();
while (itemMapIterator.hasNext()) {
Entry<String, SettingsItem> itemMapEntry = itemMapIterator.next();
String key = itemMapEntry.getKey();
if (!keepKeys.contains(key)) {
SettingsItem item = itemMapEntry.getValue();
if (item.getType().equals(Type.PARAMETER)) {
try {
remove(key, false, true);
itemMapIterator.remove();
} catch (IllegalStateException e) {
LogService.getRoot().log(Level.WARNING,
"com.rapidminer.gui.properties.SettingsItems.remove_item_error", e);
}
}
}
}
}
/** Returns true if the XML file was parsed successfully. Returns false if not. */
public boolean isStudioXmlParsedSuccessfully() {
return studioXmlParsed;
}
/**
* Creates a SettingsItem and adds it to the item map. Parent items of the type GROUP are also
* generated.
*
* @param key
* The key of the item
* @param type
* The type of the item
*
* @return The created SettingsItem
*/
public static SettingsItem createAndAddItem(String key, Type type) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Settings item has no key.");
}
SettingsItem parent = null;
// If type is parameter, choose or create the related group
if (type == Type.PARAMETER) {
String groupKey = ParameterService.getGroupKey(key);
if (!INSTANCE.containsKey(groupKey)) {
parent = createAndAddItem(groupKey, Type.GROUP);
} else {
parent = INSTANCE.get(groupKey);
}
}
// Create new SettingsItem
SettingsItem settingsItem = new SettingsItem(key, parent, type);
// Add to SettingsItems
INSTANCE.put(key, settingsItem);
return settingsItem;
}
}