/**
* 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.enocean.internal.converter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.openhab.core.items.Item;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the factory to get the converter for a state or a command. It creates
* converters for {@link Command}s and for {@link State}s in both directions.
* The {@link #getFromStateConverter(String, State)} returns an converter for
* converting a {@link State} into a protocol value, the
* {@link #getFromStateConverter(String, State)} a converter for converting a
* protocol value into a {@link State}. These can be the same class.
*
* The normal usage has three parts:
*
* Instantiate it:
* <code>private ConverterFactory converterFactory = new ConverterFactory();</code>
*
* Configure it:
* <code>converterFactory.addCommandConverter("LEVEL", OnOffType.class, OnOffPercentageCommandConverter.class);</code>
* <code>converterFactory.addStateConverter("LEVEL", PercentType.class, DoublePercentageConverter.class);</code>
*
* Use it:
* <code>StateConverter<?, ?> converter = converterFactory.getFromStateConverter(parameterAddress.getParameterKey(), newState);
* Object value = converter.convertFrom(newState);
* </code>
*
* @author Thomas Letsch (contact@thomas-letsch.de)
* @since 1.3.0
*
*/
public class ConverterFactory {
private static final Logger logger = LoggerFactory.getLogger(ConverterFactory.class);
private MatchingConverters converters = new MatchingConverters();
/**
* Adds a new {@link StateConverter} for the protocolValue.
*
* @param protocolValue
* The value key for the binding specific protocol.
* @param state
* The state it can convert.
* @param converter
* The actual converter.
*/
public void addStateConverter(String protocolValue, Class<? extends State> state,
Class<? extends StateConverter<?, ?>> converter) {
converters.addStateConverter(protocolValue, state, converter);
}
/**
* Adds a new {@link CommandConverter} for the protocolValue.
*
* @param protocolValue
* The value key for the binding specific protocol.
* @param command
* The {@link Command} it can convert.
* @param converter
* The actual converter.
*/
public void addCommandConverter(String protocolValue, Class<? extends Command> command,
Class<? extends CommandConverter<?, ?>> converter) {
converters.addCommandConverter(protocolValue, command, converter);
}
/**
* Returns the first matching converter for the given protocolValue and the
* item. It considers the possible types (states) the item can accept.
*
* This method is to be used for getting a converter for the direction from
* a protocolValue to a State.
*
* @param protocolValue
* The value key for the binding specific protocol.
* @param item
* The item for the converter.
* @return A new {@link StateConverter} to convert a value of the
* protocolKey to a state for the item.
*/
public StateConverter<?, ?> getToStateConverter(String protocolValue, Item item) {
List<Class<? extends State>> acceptedTypes = new ArrayList<Class<? extends State>>();
List<Class<? extends State>> itemTypes = new ArrayList<Class<? extends State>>(item.getAcceptedDataTypes());
List<Class<? extends State>> matchingTypes = converters.getMatchingStates(protocolValue);
for (Class<? extends State> matchingType : matchingTypes) {
for (Class<? extends State> itemType : itemTypes) {
if (itemType.isAssignableFrom(matchingType)) {
acceptedTypes.add(matchingType);
break;
}
}
}
if (acceptedTypes.isEmpty()) {
return null;
}
// Take best matching as accepted Type. Best matching is calculated by
// ordering by importance of state.
Collections.sort(acceptedTypes, new StateComparator());
Class<? extends State> acceptedType = acceptedTypes.get(acceptedTypes.size() - 1);
return (StateConverter<?, ?>) instantiate(converters.getStateConverter(protocolValue, acceptedType));
}
/**
* Returns the first matching converter for the given protocolValue and the
* state.
*
* This method is to be used for getting a converter for the direction from
* a state (the given state to be exact) to a protocolValue.
*
* @param protocolValue
* The value key for the binding specific protocol.
* @param state
* The state to convert.
* @return A new {@link StateConverter} to convert a value of the
* protocolKey to a state for the item.
*/
public <STATE extends State> StateConverter<?, ?> getFromStateConverter(String protocolValue, STATE state) {
Class<?> stateConverter = converters.getStateConverter(protocolValue, state.getClass());
if (stateConverter == null) {
return null;
}
return (StateConverter<?, ?>) instantiate(stateConverter);
}
/**
* Returns the first matching converter for the given protocolValue and the
* command.
*
* This method is to be used for getting a converter for the direction from
* a command (the given command to be exact) to a state.
*
* @param protocolValue
* The value key for the binding specific protocol.
* @param command
* The command to convert.
* @return A new {@link CommandConverter} to convert a command to a state.
*/
public <COMMAND extends Command> CommandConverter<?, ?> getCommandConverter(String protocolValue, COMMAND command) {
Class<?> commandConverter = converters.getCommandConverter(protocolValue, command.getClass());
if (commandConverter == null && command instanceof State) {
// If the command is also a State, return a converter that just
// returns the State
commandConverter = StateCommandConverter.class;
}
return (CommandConverter<?, ?>) instantiate(commandConverter);
}
private Object instantiate(Class<?> converter) {
if (converter == null) {
return null;
}
try {
return converter.newInstance();
} catch (InstantiationException e) {
logger.error("Could not instanciate " + converter, e);
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
logger.error("Could not instanciate " + converter, e);
throw new RuntimeException(e);
}
}
}