package org.dsa.iot.commons;
import org.dsa.iot.dslink.node.Permission;
import org.dsa.iot.dslink.node.actions.Action;
import org.dsa.iot.dslink.node.actions.ActionResult;
import org.dsa.iot.dslink.node.actions.Parameter;
import org.dsa.iot.dslink.node.value.Value;
import org.dsa.iot.dslink.node.value.ValueType;
import org.dsa.iot.dslink.node.value.ValueUtils;
import org.dsa.iot.dslink.util.handler.Handler;
import org.dsa.iot.dslink.util.json.JsonArray;
import java.util.*;
/**
* A special type of action that supports full persistence and parameter
* validation.
*
* A trivial use case would be for a settings action that require its settings
* to be persisted after every invocation.
*
* @author Samuel Grenier
*/
public abstract class ParameterizedAction extends Action {
private final Map<String, ParameterInfo> params = new LinkedHashMap<>();
/**
* Constructs a parameterized action that supports persistence and full
* parameter validation.
*
* @param permission Permission of the action.
*/
public ParameterizedAction(Permission permission) {
super(permission, null);
}
/**
* Using {@code actRes} to retrieve parameters will return the unvalidated
* parameter.
*
* @param actRes Action results to set output.
* @param params Validated parameters.
*/
public abstract void handle(ActionResult actRes,
Map<String, Value> params);
/**
* The {@code param} will be converted to a {@link ParameterInfo} instance
* with all of its properties copied over. By default, the copied parameter
* will be not optional, will be persistent, and will not contain a
* validator.
*
* {@inheritDoc}
*
* @see #addParameter(ParameterInfo)
*/
@Override
public ParameterizedAction addParameter(Parameter param) {
if (param == null) {
throw new NullPointerException("param");
}
String name = param.getName();
ValueType type = param.getType();
ParameterInfo copy = new ParameterInfo(name, type);
copy.setOptional(false);
copy.setPersistent(true);
copy.setDefaultValue(param.getDefault());
copy.setDescription(param.getDescription());
copy.setPlaceHolder(param.getPlaceHolder());
copy.setEditorType(param.getEditorType());
addParameter(copy);
return this;
}
/**
* @param paramInfo Parameter with extra options.
*/
public void addParameter(ParameterInfo paramInfo) {
if (paramInfo == null) {
throw new NullPointerException("paramInfo");
}
String name = paramInfo.getName();
if (params.containsKey(name)) {
String err = "Parameter name already exists: " + name;
throw new IllegalStateException(err);
}
params.put(name, paramInfo);
super.addParameter(paramInfo);
}
/**
* {@inheritDoc}
*/
@Override
public final void invoke(ActionResult actionResult) {
if (!hasPermission()) return;
List<Parameter> list = new ArrayList<>();
Map<String, Value> map = new HashMap<>();
for (ParameterInfo info : params.values()) {
String name = info.getName();
Value value = actionResult.getParameter(name);
if (!info.optional() && value == null) {
throw new RuntimeException("Missing parameter: " + name);
}
if (value != null) {
ValueUtils.checkType(name, info.getType(), value);
Handler<Value> validator = info.getValidator();
if (validator != null) {
long time = value.getTime();
validator.handle(value);
// Ensure original time stamp is preserved
value.setTime(time);
}
map.put(name, value);
}
if (info.persist()) {
info.setDefaultValue(value);
}
list.add(info);
}
setParams(list);
handle(actionResult, map);
}
/**
* {@inheritDoc}
*/
@Override
public final void setParams(Collection<Parameter> newParams) {
super.params = new JsonArray();
for (Parameter p : newParams) {
super.addParameter(p);
}
postParamsUpdate();
}
/**
* A special type of parameter that contains extra information as to how
* the parameter should behave when being validated.
*/
public static class ParameterInfo extends Parameter {
private boolean optional;
private boolean persist;
private Handler<Value> validator;
/**
* {@inheritDoc}
*/
public ParameterInfo(String name, ValueType type) {
this(name, type, null);
}
/**
* {@inheritDoc}
*/
public ParameterInfo(String name, ValueType type, Value def) {
super(name, type, def);
}
/**
* The {@code validator} can override the value or throw an
* {@link Exception} for any invalid values.
*
* @param validator Validation callback.
*/
public void setValidator(Handler<Value> validator) {
this.validator = validator;
}
/**
* @return The validator the parameter is attached to.
*/
public Handler<Value> getValidator() {
return validator;
}
/**
* If the parameter is optional then it will not be required during
* validation.
*
* @param optional Whether this parameter is optional.
*/
public void setOptional(boolean optional) {
this.optional = optional;
}
/**
* @return Whether this parameter is optional.
*/
public boolean optional() {
return optional;
}
/**
* Before the action is invoked, the persistence values will be
* configured and validated. This will ensure the parameters always
* have the correct default values.
*
* @param persist Whether this parameter is persistent.
*/
public void setPersistent(boolean persist) {
this.persist = persist;
}
/**
* @return Whether this parameter is persistent.
*/
public boolean persist() {
return persist;
}
}
}