/**
* 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.core.items;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.smarthome.core.common.registry.AbstractManagedProvider;
import org.eclipse.smarthome.core.items.ManagedItemProvider.PersistedItem;
import org.eclipse.smarthome.core.items.dto.GroupFunctionDTO;
import org.eclipse.smarthome.core.items.dto.ItemDTOMapper;
import org.eclipse.smarthome.core.storage.StorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link ManagedItemProvider} is an OSGi service, that allows to add or remove
* items at runtime by calling {@link ManagedItemProvider#addItem(Item)} or {@link ManagedItemProvider#removeItem(Item)}
* . An added item is automatically
* exposed to the {@link ItemRegistry}. Persistence of added Items is handled by
* a {@link StorageService}. Items are being restored using the given {@link ItemFactory}s.
*
* @author Dennis Nobel - Initial contribution, added support for GroupItems
* @author Thomas Eichstaedt-Engelen
* @author Kai Kreuzer - improved return values
* @author Alex Tugarev - added tags
*/
public class ManagedItemProvider extends AbstractManagedProvider<Item, String, PersistedItem> implements ItemProvider {
public static class PersistedItem {
public String baseItemType;
public List<String> groupNames;
public String itemType;
public Set<String> tags;
public String label;
public String category;
public String functionName;
public List<String> functionParams;
}
private static final String ITEM_TYPE_GROUP = "Group";
private final Logger logger = LoggerFactory.getLogger(ManagedItemProvider.class);
private Collection<ItemFactory> itemFactories = new CopyOnWriteArrayList<ItemFactory>();
private final Map<String, PersistedItem> failedToCreate = new ConcurrentHashMap<>();
/**
* Removes an item and it´s member if recursive flag is set to true.
*
* @param itemName
* item name to remove
* @param recursive
* if set to true all members of the item will be removed, too.
* @return
* removed item or null if no item with that name exists
*/
public Item remove(String itemName, boolean recursive) {
Item item = get(itemName);
if (recursive && item instanceof GroupItem) {
List<String> members = getMemberNamesRecursively((GroupItem) item, getAll());
for (String member : members) {
this.remove(member);
}
}
if (item != null) {
this.remove(item.getName());
return item;
} else {
return null;
}
}
private List<String> getMemberNamesRecursively(GroupItem groupItem, Collection<Item> allItems) {
List<String> memberNames = new ArrayList<>();
for (Item item : allItems) {
if (item.getGroupNames().contains(groupItem.getName())) {
memberNames.add(item.getName());
if (item instanceof GroupItem) {
memberNames.addAll(getMemberNamesRecursively((GroupItem) item, allItems));
}
}
}
return memberNames;
}
private GenericItem createItem(String itemType, String itemName) {
for (ItemFactory factory : this.itemFactories) {
GenericItem item = factory.createItem(itemType, itemName);
if (item != null) {
return item;
}
}
logger.debug("Couldn't find ItemFactory for item '{}' of type '{}'", itemName, itemType);
return null;
}
/**
* Translates the Items class simple name into a type name understandable by
* the {@link ItemFactory}s.
*
* @param item
* the Item to translate the name
* @return the translated ItemTypeName understandable by the {@link ItemFactory}s
*/
private String toItemFactoryName(Item item) {
return item.getType();
}
protected void addItemFactory(ItemFactory itemFactory) {
itemFactories.add(itemFactory);
if (failedToCreate.size() > 0) {
// retry failed creation attempts
Iterator<Entry<String, PersistedItem>> iterator = failedToCreate.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, PersistedItem> entry = iterator.next();
String itemName = entry.getKey();
PersistedItem persistedItem = entry.getValue();
ActiveItem item = itemFactory.createItem(persistedItem.itemType, itemName);
if (item != null) {
iterator.remove();
configureItem(persistedItem, item);
notifyListenersAboutAddedElement(item);
} else {
logger.debug("The added item factory '{}' still could not instantiate item '{}'.", itemFactory,
itemName);
}
}
if (failedToCreate.isEmpty()) {
logger.info("Finished loading the items which could not have been created before.");
}
}
}
@Override
protected String getKey(Item element) {
return element.getName();
}
@Override
protected String getStorageName() {
return Item.class.getName();
}
@Override
protected String keyToString(String key) {
return key;
}
protected void removeItemFactory(ItemFactory itemFactory) {
itemFactories.remove(itemFactory);
}
@Override
protected Item toElement(String itemName, PersistedItem persistedItem) {
ActiveItem item = null;
if (persistedItem.itemType.equals(ITEM_TYPE_GROUP)) {
if (persistedItem.baseItemType != null) {
GenericItem baseItem = createItem(persistedItem.baseItemType, itemName);
if (persistedItem.functionName != null) {
GroupFunction function = getGroupFunction(persistedItem, baseItem);
item = new GroupItem(itemName, baseItem, function);
} else {
item = new GroupItem(itemName, baseItem, new GroupFunction.Equality());
}
} else {
item = new GroupItem(itemName);
}
} else {
item = createItem(persistedItem.itemType, itemName);
}
configureItem(persistedItem, item);
if (item == null) {
failedToCreate.put(itemName, persistedItem);
logger.debug("Couldn't restore item '{}' of type '{}' ~ there is no appropriate ItemFactory available.",
itemName, persistedItem.itemType);
}
return item;
}
private GroupFunction getGroupFunction(PersistedItem persistedItem, GenericItem baseItem) {
GroupFunctionDTO functionDTO = new GroupFunctionDTO();
functionDTO.name = persistedItem.functionName;
if (persistedItem.functionParams != null) {
functionDTO.params = persistedItem.functionParams.toArray(new String[persistedItem.functionParams.size()]);
}
return ItemDTOMapper.mapFunction(baseItem, functionDTO);
}
private void configureItem(PersistedItem persistedItem, ActiveItem item) {
if (item != null) {
List<String> groupNames = persistedItem.groupNames;
if (groupNames != null) {
for (String groupName : groupNames) {
item.addGroupName(groupName);
}
}
Set<String> tags = persistedItem.tags;
if (tags != null) {
for (String tag : tags) {
item.addTag(tag);
}
}
item.setLabel(persistedItem.label);
item.setCategory(persistedItem.category);
}
}
@Override
protected PersistedItem toPersistableElement(Item item) {
PersistedItem persistedItem = new PersistedItem();
if (item instanceof GroupItem) {
GroupItem groupItem = (GroupItem) item;
String baseItemType = null;
Item baseItem = groupItem.getBaseItem();
if (baseItem != null) {
baseItemType = toItemFactoryName(baseItem);
}
persistedItem.itemType = ITEM_TYPE_GROUP;
persistedItem.baseItemType = baseItemType;
addFunctionToPersisedItem(persistedItem, groupItem);
} else {
String itemType = toItemFactoryName(item);
persistedItem.itemType = itemType;
}
persistedItem.label = item.getLabel();
persistedItem.groupNames = new ArrayList<>(item.getGroupNames());
persistedItem.tags = new HashSet<>(item.getTags());
persistedItem.category = item.getCategory();
return persistedItem;
}
private void addFunctionToPersisedItem(PersistedItem persistedItem, GroupItem groupItem) {
if (groupItem.getFunction() != null) {
GroupFunctionDTO functionDTO = ItemDTOMapper.mapFunction(groupItem.getFunction());
persistedItem.functionName = functionDTO.name;
if (functionDTO.params != null) {
persistedItem.functionParams = Arrays.asList(functionDTO.params);
}
}
}
}