/**
* Copyright (c) 2014-2017 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.eclipse.smarthome.model.thing.internal;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.smarthome.core.common.registry.AbstractProvider;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.link.ItemChannelLink;
import org.eclipse.smarthome.core.thing.link.ItemChannelLinkProvider;
import org.eclipse.smarthome.model.core.EventType;
import org.eclipse.smarthome.model.core.ModelRepository;
import org.eclipse.smarthome.model.core.ModelRepositoryChangeListener;
import org.eclipse.smarthome.model.item.BindingConfigParseException;
import org.eclipse.smarthome.model.item.BindingConfigReader;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
/**
* {@link GenericItemChannelLinkProvider} link items to channel by reading bindings with type "channel".
*
* @author Oliver Libutzki - Initial contribution
* @author Alex Tugarev - Added parsing of multiple Channel UIDs
*
*/
public class GenericItemChannelLinkProvider extends AbstractProvider<ItemChannelLink> implements BindingConfigReader,
ItemChannelLinkProvider, ModelRepositoryChangeListener {
/** caches binding configurations. maps itemNames to {@link BindingConfig}s */
protected Map<String, Set<ItemChannelLink>> itemChannelLinkMap = new ConcurrentHashMap<>();
/**
* stores information about the context of items. The map has this content
* structure: context -> Set of Item names
*/
protected Map<String, Set<String>> contextMap = new ConcurrentHashMap<>();
@SuppressWarnings("unused")
private ModelRepository modelRepository = null;
private Set<String> previousItemNames;
@Override
public String getBindingType() {
return "channel";
}
@Override
public void validateItemType(String itemType, String bindingConfig) throws BindingConfigParseException {
// all item types are allowed
}
@Override
public void processBindingConfiguration(String context, String itemType, String itemName, String bindingConfig)
throws BindingConfigParseException {
String[] uids = bindingConfig.split(",");
if (uids.length == 0) {
throw new BindingConfigParseException(
"At least one Channel UID should be provided: <bindingID>.<thingTypeId>.<thingId>.<channelId>");
}
for (String uid : uids) {
createItemChannelLink(context, itemName, uid.trim());
}
}
private void createItemChannelLink(String context, String itemName, String channelUID)
throws BindingConfigParseException {
ChannelUID channelUIDObject = null;
try {
channelUIDObject = new ChannelUID(channelUID);
} catch (IllegalArgumentException e) {
throw new BindingConfigParseException(e.getMessage());
}
ItemChannelLink itemChannelLink = new ItemChannelLink(itemName, channelUIDObject);
Set<String> itemNames = contextMap.get(context);
if (itemNames == null) {
itemNames = new HashSet<>();
contextMap.put(context, itemNames);
}
itemNames.add(itemName);
if(previousItemNames != null ) {
previousItemNames.remove(itemName);
}
Set<ItemChannelLink> links = itemChannelLinkMap.get(itemName);
if (links == null) {
itemChannelLinkMap.put(itemName, links = new HashSet<>());
}
if (!links.contains(itemChannelLink)) {
links.add(itemChannelLink);
notifyListenersAboutAddedElement(itemChannelLink);
} else {
notifyListenersAboutUpdatedElement(itemChannelLink, itemChannelLink);
}
}
@Override
public void startConfigurationUpdate(String context) {
previousItemNames = contextMap.get(context);
}
@Override
public void stopConfigurationUpdate(String context) {
if (previousItemNames != null) {
for (String itemName : previousItemNames) {
// we remove all binding configurations that were not processed
Set<ItemChannelLink> links = itemChannelLinkMap.remove(itemName);
if (links != null) {
for (ItemChannelLink removedItemChannelLink : links) {
notifyListenersAboutRemovedElement(removedItemChannelLink);
}
}
}
contextMap.remove(context);
}
}
@Override
public Collection<ItemChannelLink> getAll() {
return Lists.newLinkedList(Iterables.concat(itemChannelLinkMap.values()));
}
public void setModelRepository(ModelRepository modelRepository) {
this.modelRepository = modelRepository;
modelRepository.addModelRepositoryChangeListener(this);
}
public void unsetModelRepository(ModelRepository modelRepository) {
modelRepository.removeModelRepositoryChangeListener(this);
this.modelRepository = null;
}
@Override
public void modelChanged(String modelName, EventType type) {
if (modelName.endsWith("items")) {
switch (type) {
case ADDED:
startConfigurationUpdate(modelName);
break;
case MODIFIED:
startConfigurationUpdate(modelName);
break;
case REMOVED:
startConfigurationUpdate(modelName);
stopConfigurationUpdate(modelName);
break;
}
}
}
}