/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.lcn.internal;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.lcn.LcnBindingProvider;
import org.openhab.binding.lcn.common.LcnAddrMod;
import org.openhab.binding.lcn.input.Input;
import org.openhab.binding.lcn.input.Mod;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.StringItem;
import org.openhab.core.library.types.StringType;
import org.openhab.core.transform.TransformationException;
import org.openhab.core.transform.TransformationHelper;
import org.openhab.core.transform.TransformationService;
import org.openhab.core.types.Command;
import org.openhab.core.types.TypeParser;
import org.openhab.model.item.binding.AbstractGenericBindingProvider;
import org.openhab.model.item.binding.BindingConfigParseException;
/**
* Manages item-configurations.
*
* @author Tobias J�ttner
*/
public class LcnGenericBindingProvider extends AbstractGenericBindingProvider implements LcnBindingProvider {
/** Pattern used to parse bindings in general. */
private static final Pattern PATTERN_BINDING_GENERAL = Pattern.compile("(\\[.*?\\])*");
/** Pattern used to parse bindings which are bound to openHAB commands. */
private static final Pattern PATTERN_BINDING_WITH_OPENHAB = Pattern.compile("\\[(.*?):(.*?):\'?(.*?)\'?\\]");
/** Pattern used to parse bindings which don't have an openHAB command. */
private static final Pattern PATTERN_BINDING_PURE = Pattern.compile("\\[(.*):\'?(.*?)\'?\\]");
/** Pattern to identify mappings in item bindings. */
private static final Pattern PATTERN_MAPPING = Pattern.compile("(.*?)\\((.*)\\)");
/**
* Stores all item names related to an LCN module address.
* Used for optimization.
* This map is never cleaned and might contain out-dated mappings or items that no longer exist.
*/
private final HashMap<LcnAddrMod, HashSet<String>> itemNamesByModulCache = new HashMap<LcnAddrMod, HashSet<String>>();
/**
* Item processing for the LCN bindings.
* {@inheritDoc}
*/
@Override
public void processBindingConfiguration(String context, Item item, String bindingConfig)
throws BindingConfigParseException {
super.processBindingConfiguration(context, item, bindingConfig);
Matcher matcher = PATTERN_BINDING_GENERAL.matcher(bindingConfig);
if (!matcher.matches()) {
throw new BindingConfigParseException(bindingConfig + "' contains no valid binding!");
}
matcher.reset();
LcnBindingConfig bc = new LcnBindingConfig(item);
while (matcher.find()) {
String binding = matcher.group(1);
if (binding != null && !binding.trim().isEmpty()) {
String openHabCmd = null;
String connId, lcnTarget;
Matcher openHabMatcher = PATTERN_BINDING_WITH_OPENHAB.matcher(binding);
Matcher pureMatcher = PATTERN_BINDING_PURE.matcher(binding);
if (openHabMatcher.matches()) {
openHabCmd = openHabMatcher.group(1);
connId = openHabMatcher.group(2);
lcnTarget = openHabMatcher.group(3);
} else if (pureMatcher.matches()) {
connId = pureMatcher.group(1);
lcnTarget = pureMatcher.group(2);
} else {
throw new BindingConfigParseException("Invalid binding configuration for " + binding + "!");
}
String lcnShort = resolveMappings(lcnTarget, openHabCmd);
if (lcnShort == null || lcnShort.equals(openHabCmd)) {
lcnShort = lcnTarget;
}
Command cmd = openHabCmd == null
? TypeParser.parseCommand(new StringItem("").getAcceptedCommandTypes(), "")
: openHabCmd.equals("%i") ? new StringType("%i")
: TypeParser.parseCommand(item.getAcceptedCommandTypes(), openHabCmd);
bc.add(new LcnBindingConfig.Mapping(cmd, connId, lcnShort));
}
}
// Finished
this.addBindingConfig(item, bc);
for (LcnAddrMod addr : bc.getRelatedModules()) {
HashSet<String> l = this.itemNamesByModulCache.get(addr);
if (l == null) {
l = new HashSet<String>();
this.itemNamesByModulCache.put(addr, l);
}
l.add(item.getName());
}
}
/** {inheritDoc} */
@Override
public String getBindingType() {
return "lcn";
}
/** {inheritDoc} */
@Override
public void validateItemType(Item item, String bindingConfig) throws BindingConfigParseException {
// Everything is accepted
}
/**
* Resolves LCN commands (with mappings) to plain commands.
*
* @param lcnTarget the target or a mapping
* @param openHABcmd the command send by openHAB
* @return the resolved result (can be null)
*/
private static String resolveMappings(String lcnTarget, String openHABcmd) {
String result = null;
Matcher matcher = PATTERN_MAPPING.matcher(lcnTarget);
if (!matcher.matches()) {
result = lcnTarget;
} else {
matcher.reset();
matcher.find();
String s1 = matcher.group(1);
String s2 = matcher.group(2);
TransformationService transformationService = TransformationHelper
.getTransformationService(LcnBindingActivator.getContext(), s1);
if (transformationService != null) {
try {
result = transformationService.transform(s2, openHABcmd);
} catch (TransformationException e) {
result = lcnTarget;
}
} else {
result = lcnTarget;
}
}
return result;
}
/**
* Finds an LCN item by name.
*
* @param itemName the item's name to search for
* @return the {@link LcnBindingConfig} or null
*/
public LcnBindingConfig getLcnItemConfig(String itemName) {
return (LcnBindingConfig) this.bindingConfigs.get(itemName);
}
/**
* Gets all LCN item names.
*
* @return the list of item names
*/
@Override
public Collection<String> getItemNames() {
return this.bindingConfigs.keySet();
}
/**
* Gets all LCN item names associated with the given {@link Input}.
* Might return items that no longer exist.
*
* @param pchkInput the input from LCN-PCHK
* @return the list of item names (note that some items might no longer exist)
*/
public Collection<String> getItemNamesForPchkInput(Input pchkInput) {
// Optimization: If the PckInput has a module address, we get a reduced list of item names from cache
if (pchkInput instanceof Mod) {
LcnAddrMod addr = ((Mod) pchkInput).getLogicalSourceAddr();
if (addr.isValid() && this.itemNamesByModulCache.containsKey(addr)) {
return this.itemNamesByModulCache.get(addr);
}
}
return this.getItemNames();
}
}