/**
* 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.concurrent.Callable;
import java.util.logging.Level;
import org.w3c.dom.Element;
import com.rapidminer.MacroHandler;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorVersion;
import com.rapidminer.operator.ports.InputPort;
import com.rapidminer.operator.ports.metadata.MetaData;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.XMLException;
import com.rapidminer.tools.expression.ExpressionParserBuilder;
/**
* This attribute type supports the user by letting him define an expression with a user interface
* known from calculators.
*
* For knowing attribute names before process execution a valid meta data transformation must be
* performed. Otherwise the user might type in the name, instead of choosing.
*
* @author Ingo Mierswa
*/
public class ParameterTypeExpression extends ParameterTypeString {
private static final long serialVersionUID = -1938925853519339382L;
private static final String ATTRIBUTE_INPUT_PORT = "port-name";
private transient InputPort inPort;
private Callable<OperatorVersion> operatorVersion;
/**
* A simple functional which allows to query the current operator compatibility level.
*/
public static final class OperatorVersionCallable implements Callable<OperatorVersion> {
private final Operator op;
/**
* Constructor for the {@link OperatorVersionCallable}.
*
* @param op
* the operator. Must not be {@code null}
*/
public OperatorVersionCallable(Operator op) {
if (op == null) {
throw new IllegalArgumentException("Operator must not be null");
}
this.op = op;
}
@Override
public OperatorVersion call() {
return op.getCompatibilityLevel();
}
}
public ParameterTypeExpression(Element element) throws XMLException {
super(element);
}
/**
* This constructor will generate a ParameterType that does not use the {@link MetaData} of an
* associated {@link InputPort} to verify the expressions.
*
* @param key
* @param description
*
* @deprecated use {@link #ParameterTypeExpression(String, String, OperatorVersionCallable)}
* instead
*/
@Deprecated
public ParameterTypeExpression(final String key, String description) {
this(key, description, null, false);
}
/**
* This constructor will generate a ParameterType that does not use the {@link MetaData} of an
* associated {@link InputPort} to verify the expressions.
*
* @param key
* the parameter key
* @param description
* the parameter description
* @param operatorVersion
* a functional which allows to query the current operator version. Must not be
* {@code null} and must not return null
*/
public ParameterTypeExpression(final String key, String description, OperatorVersionCallable operatorVersion) {
this(key, description, null, false, operatorVersion);
}
public ParameterTypeExpression(final String key, String description, InputPort inPort) {
this(key, description, inPort, false);
}
public ParameterTypeExpression(final String key, String description, InputPort inPort, boolean optional, boolean expert) {
this(key, description, inPort, optional);
setExpert(expert);
}
public ParameterTypeExpression(final String key, String description, final InputPort inPort, boolean optional) {
this(key, description, inPort, optional, new Callable<OperatorVersion>() {
@Override
public OperatorVersion call() throws Exception {
if (inPort != null) {
return inPort.getPorts().getOwner().getOperator().getCompatibilityLevel();
} else {
// callers that do not provide an input port are not be able to use the
// expression parser functions
return new OperatorVersion(6, 4, 0);
}
}
});
if (inPort == null) {
LogService.getRoot().log(Level.INFO, "com.rapidminer.parameter.ParameterTypeExpression.no_input_port_provided");
}
}
private ParameterTypeExpression(final String key, String description, InputPort inPort, boolean optional,
Callable<OperatorVersion> operatorVersion) {
super(key, description, optional);
if (operatorVersion == null) {
throw new IllegalArgumentException("Operator version parameter must not be null");
}
this.inPort = inPort;
this.operatorVersion = operatorVersion;
}
@Override
public Object getDefaultValue() {
return "";
}
/**
* Returns the input port associated with this ParameterType. This might be null!
*/
public InputPort getInputPort() {
return inPort;
}
@Override
protected void writeDefinitionToXML(Element typeElement) {
super.writeDefinitionToXML(typeElement);
typeElement.setAttribute(ATTRIBUTE_INPUT_PORT, inPort.getName());
}
@Override
public String substituteMacros(String parameterValue, MacroHandler mh) throws UndefinedParameterError {
OperatorVersion version;
try {
version = operatorVersion.call();
} catch (Exception e) {
// cannot happen, throw error nevertheless
throw new UndefinedParameterError(getKey());
}
if (version != null && version.isAtMost(ExpressionParserBuilder.OLD_EXPRESSION_PARSER_FUNCTIONS)) {
// do not replace macros in case the compatibility level is at most 6.4
return super.substituteMacros(parameterValue, mh);
} else {
return parameterValue;
}
}
@Override
public String substitutePredefinedMacros(String parameterValue, Operator operator) throws UndefinedParameterError {
OperatorVersion version;
try {
version = operatorVersion.call();
} catch (Exception e) {
// cannot happen, throw error nevertheless
throw new UndefinedParameterError(getKey());
}
if (version != null && version.isAtMost(ExpressionParserBuilder.OLD_EXPRESSION_PARSER_FUNCTIONS)) {
// do not replace macros in case the compatibility level is at most 6.4
return super.substitutePredefinedMacros(parameterValue, operator);
} else {
return parameterValue;
}
}
}