/** * 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.parameter; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.rapidminer.MacroHandler; import com.rapidminer.io.process.XMLTools; import com.rapidminer.operator.Operator; import com.rapidminer.tools.Tools; import com.rapidminer.tools.XMLException; /** * A parameter type for parameter lists. Operators ask for the list of the specified values with * {@link com.rapidminer.operator.Operator#getParameterList(String)}. Please note that in principle * arbitrary parameter types can be used for the list values. Internally, however, all values are * transformed to strings. Therefore, operators retrieving values from non-string lists (for example * for a parameter type category) have to transform the values themself, e.g. with the following * code:<br/> * <br/> * * <code>int index = ((ParameterTypeCategory)((ParameterTypeList)getParameters().getParameterType(PARAMETER_LIST)).getValueType()).getIndex(pair[1]);</code> * * @author Ingo Mierswa, Simon Fischer */ public class ParameterTypeList extends CombinedParameterType { private static final long serialVersionUID = -6101604413822993455L; private static final String ELEMENT_KEY_TYPE = "KeyType"; private static final String ELEMENT_VALUE_TYPE = "ValueType"; private static final String ELEMENT_DEFAULT_ENTRIES = "DefaultEntries"; private static final String ELEMENT_ENTRY = "Entry"; private static final String ATTRIBUTE_KEY = "key"; private static final String ATTRIBUTE_VALUE = "value"; private List<String[]> defaultList = new LinkedList<>(); private final ParameterType valueType; private final ParameterType keyType; public ParameterTypeList(Element element) throws XMLException { super(element); valueType = ParameterType.createType(XMLTools.getChildElement(element, ELEMENT_VALUE_TYPE, true)); keyType = ParameterType.createType(XMLTools.getChildElement(element, ELEMENT_KEY_TYPE, true)); // now default values Element defaultEntriesElement = XMLTools.getChildElement(element, ELEMENT_DEFAULT_ENTRIES, true); for (Element entryElement : XMLTools.getChildElements(defaultEntriesElement, ELEMENT_ENTRY)) { defaultList.add(new String[] { entryElement.getAttribute(ATTRIBUTE_KEY), entryElement.getAttribute(ATTRIBUTE_VALUE) }); } } @Deprecated /** * This constructor is deprecated, because it does not provide enough information for user guidance */ public ParameterTypeList(String key, String description, ParameterType valueType) { this(key, description, valueType, new LinkedList<String[]>()); } @Deprecated /** * This constructor is deprecated, because it does not provide enough information for user guidance */ public ParameterTypeList(String key, String description, ParameterType valueType, List<String[]> defaultList) { super(key, description); this.defaultList = defaultList; this.valueType = valueType; this.keyType = new ParameterTypeString(key, description); if (valueType.getDescription() == null) { valueType.setDescription(description); } } public ParameterTypeList(String key, String description, ParameterType keyType, ParameterType valueType, boolean expert) { this(key, description, keyType, valueType, new LinkedList<String[]>()); setExpert(expert); } public ParameterTypeList(String key, String description, ParameterType keyType, ParameterType valueType) { this(key, description, keyType, valueType, new LinkedList<String[]>()); } public ParameterTypeList(String key, String description, ParameterType keyType, ParameterType valueType, List<String[]> defaultList, boolean expert) { this(key, description, keyType, valueType, defaultList); setExpert(expert); } public ParameterTypeList(String key, String description, ParameterType keyType, ParameterType valueType, List<String[]> defaultList) { super(key, description, keyType, valueType); this.defaultList = defaultList; this.valueType = valueType; this.keyType = keyType; } public ParameterType getValueType() { return valueType; } public ParameterType getKeyType() { return keyType; } @Override public List<String[]> getDefaultValue() { return defaultList; } @SuppressWarnings("unchecked") @Override // TODO: Introduce Typing?? public void setDefaultValue(Object defaultValue) { this.defaultList = (List<String[]>) defaultValue; } /** Returns false. */ @Override public boolean isNumerical() { return false; } @Override public Element getXML(String key, String value, boolean hideDefault, Document doc) { Element element = doc.createElement("list"); element.setAttribute("key", key); List<String[]> list = null; if (value != null) { list = transformString2List(value); } else { list = getDefaultValue(); } if (list != null) { for (Object object : list) { Object[] entry = (Object[]) object; element.appendChild(valueType.getXML((String) entry[0], entry[1].toString(), false, doc)); } } return element; } /** @deprecated Replaced by DOM. */ @Override @Deprecated public String getXML(String indent, String key, String value, boolean hideDefault) { StringBuffer result = new StringBuffer(); result.append(indent + "<list key=\"" + key + "\">" + Tools.getLineSeparator()); if (value != null) { List<String[]> list = Parameters.transformString2List(value); Iterator<String[]> i = list.iterator(); while (i.hasNext()) { Object[] current = i.next(); result.append(valueType.getXML(indent + " ", (String) current[0], current[1].toString(), false)); } } else { List<String[]> defaultValue = getDefaultValue(); if (defaultValue != null) { List<String[]> defaultList = defaultValue; Iterator<String[]> i = defaultList.iterator(); while (i.hasNext()) { Object[] current = i.next(); result.append(valueType.getXML(indent + " ", (String) current[0], current[1].toString(), false)); } } } result.append(indent + "</list>" + Tools.getLineSeparator()); return result.toString(); } @Override public String getRange() { return "list"; } @SuppressWarnings("unchecked") @Override public String toString(Object value) { if (value instanceof String) { return transformList2String(transformString2List(value.toString())); } else { return transformList2String((List<String[]>) value); } } public static String transformList2String(List<String[]> parameterList) { StringBuffer result = new StringBuffer(); Iterator<String[]> i = parameterList.iterator(); boolean first = true; while (i.hasNext()) { String[] objects = i.next(); if (objects.length != 2) { continue; } String firstToken = objects[0]; String secondToken = objects[1]; if (!first) { result.append(Parameters.RECORD_SEPARATOR); } if (secondToken != null) { if (firstToken != null) { result.append(firstToken); } result.append(Parameters.PAIR_SEPARATOR); if (secondToken != null) { result.append(secondToken); } } first = false; } return result.toString(); } public static List<String[]> transformString2List(String listString) { List<String[]> result = new LinkedList<>(); String[] splittedList = listString.split(Character.valueOf(Parameters.RECORD_SEPARATOR).toString()); for (String record : splittedList) { if (record.length() > 0) { String[] pair = record.split(Character.valueOf(Parameters.PAIR_SEPARATOR).toString()); if (pair.length == 2 && pair[0].length() > 0 && pair[1].length() > 0) { result.add(new String[] { pair[0], pair[1] }); } } } return result; } @Override public String notifyOperatorRenaming(String oldOperatorName, String newOperatorName, String parameterValue) { List<String[]> list = transformString2List(parameterValue); for (String[] pair : list) { pair[0] = keyType.notifyOperatorRenaming(oldOperatorName, newOperatorName, pair[0]); pair[1] = valueType.notifyOperatorRenaming(oldOperatorName, newOperatorName, pair[1]); } return transformList2String(list); } @Override public String substituteMacros(String parameterValue, MacroHandler mh) throws UndefinedParameterError { if (parameterValue.indexOf("%{") == -1) { return parameterValue; } List<String[]> list = transformString2List(parameterValue); List<String[]> result = new LinkedList<>(); for (String[] entry : list) { result.add(new String[] { getKeyType().substituteMacros(entry[0], mh), getValueType().substituteMacros(entry[1], mh) }); } return transformList2String(result); } @Override public String substitutePredefinedMacros(String parameterValue, Operator operator) throws UndefinedParameterError { if (parameterValue.indexOf("%{") == -1) { return parameterValue; } List<String[]> list = transformString2List(parameterValue); List<String[]> result = new LinkedList<>(); for (String[] entry : list) { result.add(new String[] { getKeyType().substitutePredefinedMacros(entry[0], operator), getValueType().substitutePredefinedMacros(entry[1], operator) }); } return transformList2String(result); } /** * {@inheritDoc} * * @return {@code true} if either the key parameter or the value parameter or both are * sensitive; {@code false} otherwise */ @Override public boolean isSensitive() { boolean keySensitive = getKeyType() != null ? getKeyType().isSensitive() : false; boolean valueSensitive = getValueType() != null ? getValueType().isSensitive() : false; return keySensitive || valueSensitive; } @Override protected void writeDefinitionToXML(Element typeElement) { Element keyTypeElement = XMLTools.addTag(typeElement, ELEMENT_KEY_TYPE); keyType.writeDefinitionToXML(keyTypeElement); Element valueTypeElement = XMLTools.addTag(typeElement, ELEMENT_VALUE_TYPE); valueType.writeDefinitionToXML(valueTypeElement); // now default list Element defaultEntriesElement = XMLTools.addTag(typeElement, ELEMENT_DEFAULT_ENTRIES); for (String[] pair : defaultList) { Element defaultEntryElement = XMLTools.addTag(defaultEntriesElement, ELEMENT_ENTRY); defaultEntryElement.setAttribute(ATTRIBUTE_KEY, pair[0]); defaultEntryElement.setAttribute(ATTRIBUTE_VALUE, pair[1]); } } }