/*
* AndFHEM - Open Source Android application to control a FHEM home automation
* server.
*
* Copyright (c) 2011, Matthias Klass or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU GENERAL PUBLIC LICENSE, as published by the Free Software Foundation.
*
* 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 GENERAL PUBLIC LICENSE
* for more details.
*
* You should have received a copy of the GNU GENERAL PUBLIC LICENSE
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package li.klass.fhem.service.room.xmllist;
import org.joda.time.DateTime;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import li.klass.fhem.service.deviceConfiguration.DeviceConfigurationProvider;
import li.klass.fhem.util.ValueDescriptionUtil;
import static com.google.common.base.Strings.isNullOrEmpty;
import static li.klass.fhem.service.room.xmllist.DeviceNode.DeviceNodeType.ATTR;
import static li.klass.fhem.service.room.xmllist.DeviceNode.DeviceNodeType.STATE;
import static li.klass.fhem.util.ValueExtractUtil.extractLeadingDouble;
import static li.klass.fhem.util.ValueExtractUtil.extractLeadingInt;
@Singleton
public class Sanitiser {
private static final Logger LOGGER = LoggerFactory.getLogger(Sanitiser.class);
@Inject
DeviceConfigurationProvider deviceConfigurationProvider;
@Inject
public Sanitiser() {
}
public DeviceNode sanitise(String deviceType, DeviceNode deviceNode) {
try {
JSONObject deviceOptions = optionsFor(deviceType);
return sanitise(deviceNode, deviceOptions);
} catch (Exception e) {
LOGGER.error("cannot sanitise {}", deviceNode);
return deviceNode;
}
}
public void sanitise(String deviceType, XmlListDevice xmlListDevice) {
try {
JSONObject typeOptions = optionsFor(deviceType);
if (typeOptions == null) return;
JSONObject generalOptions = typeOptions.optJSONObject("__general__");
if (generalOptions == null) return;
handleGeneral(xmlListDevice, generalOptions);
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
private void handleGeneral(XmlListDevice xmlListDevice, JSONObject generalOptions) throws JSONException {
handleGeneralAttributesIfNotPresent(generalOptions, xmlListDevice);
handleGeneralStatesIfNotPresent(generalOptions, xmlListDevice);
handleGeneralAttributeAddIfModelDoesNotMatch(xmlListDevice, generalOptions);
}
private void handleGeneralAttributeAddIfModelDoesNotMatch(XmlListDevice xmlListDevice, JSONObject generalOptions) throws JSONException {
JSONObject config = generalOptions.optJSONObject("addAttributeIfModelDoesNotMatch");
if (config == null) return;
JSONArray models = config.getJSONArray("models");
DeviceNode modelNode = xmlListDevice.getAttributes().get("model");
String model = modelNode == null ? null : modelNode.getValue();
for (int i = 0; i < models.length(); i++) {
if (models.getString(i).equalsIgnoreCase(model)) {
return;
}
}
xmlListDevice.setAttribute(config.getString("key"), config.getString("value"));
}
private void handleGeneralAttributesIfNotPresent(JSONObject generalOptions, XmlListDevice xmlListDevice) throws JSONException {
JSONArray attributes = generalOptions.optJSONArray("addAttributesIfNotPresent");
if (attributes == null) return;
for (int i = 0; i < attributes.length(); i++) {
JSONObject object = attributes.getJSONObject(i);
String key = object.getString("key");
String value = object.getString("value");
if (!xmlListDevice.getAttributes().containsKey(key)) {
xmlListDevice.getAttributes().put(key, new DeviceNode(ATTR, key, value, (DateTime) null));
}
}
}
private void handleGeneralStatesIfNotPresent(JSONObject generalOptions, XmlListDevice xmlListDevice) throws JSONException {
JSONArray states = generalOptions.optJSONArray("addStatesIfNotPresent");
if (states == null) return;
for (int i = 0; i < states.length(); i++) {
JSONObject object = states.getJSONObject(i);
String key = object.getString("key");
String value = object.getString("value");
if (!xmlListDevice.getStates().containsKey(key)) {
xmlListDevice.getStates().put(key, new DeviceNode(STATE, key, value, (DateTime) null));
}
}
}
private DeviceNode sanitise(DeviceNode deviceNode, JSONObject deviceOptions) {
JSONObject attributeOptions = deviceOptions.optJSONObject(deviceNode.getKey());
if (attributeOptions == null) {
return deviceNode;
}
String key = deviceNode.getKey();
String value = deviceNode.getValue();
DateTime measured = deviceNode.getMeasured();
DeviceNode.DeviceNodeType type = deviceNode.getType();
value = value.replaceAll("°", "°");
value = handleReplaceAll(attributeOptions, value);
value = handleReplace(attributeOptions, value);
value = handleExtract(attributeOptions, value);
value = handleAppend(attributeOptions, value);
return new DeviceNode(type, key, value, measured);
}
private String handleReplaceAll(JSONObject attributeOptions, String value) {
JSONArray replaceAll = attributeOptions.optJSONArray("replaceAll");
if (replaceAll != null) {
for (int i = 0; i < replaceAll.length(); i++) {
JSONObject conf = replaceAll.optJSONObject(i);
String toSearch = conf.optString("search");
String searchReplace = conf.optString("replace");
value = value.replaceAll(toSearch, searchReplace);
}
}
return value.trim();
}
private String handleReplace(JSONObject attributeOptions, String value) {
String replace = attributeOptions.optString("replace");
String replaceBy = attributeOptions.optString("replaceBy");
replaceBy = replaceBy == null ? "" : replaceBy;
if (!isNullOrEmpty(replace)) {
value = value.replaceAll(replace, replaceBy);
}
return value.trim();
}
private String handleAppend(JSONObject attributeOptions, String value) {
String append = attributeOptions.optString("append");
if (!isNullOrEmpty(append)) {
value = ValueDescriptionUtil.append(value, append);
}
return value;
}
private String handleExtract(JSONObject attributeOptions, String value) {
String extract = attributeOptions.optString("extract");
if (!isNullOrEmpty(extract)) {
switch (extract) {
case "double":
int extractDigits = attributeOptions.optInt("extractDigits", 0);
double result = extractDigits != 0 ?
extractLeadingDouble(value, extractDigits) :
extractLeadingDouble(value);
int divFactor = attributeOptions.optInt("extractDivideBy", 0);
if (divFactor != 0) {
result = Math.round(result / 1000d);
}
return String.valueOf(result);
case "int":
return String.valueOf(extractLeadingInt(value));
}
}
return value;
}
private JSONObject optionsFor(String type) {
return deviceConfigurationProvider.sanitiseConfigurationFor(type).or(new JSONObject());
}
}