* 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.opendaylight.openflowplugin.openflow.md.core.sal.convertor;
-import java.util.ArrayList;
+import com.google.common.annotations.VisibleForTesting;
+import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
+import java.util.Collections;
import java.util.Map;
-import java.util.Objects;
+import java.util.Map.Entry;
import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.Convertor;
import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.ConvertorData;
-import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.ParametrizedConvertor;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Manages various convertors and allows to use them all in one generic way
+ * Manages various convertors and allows to use them all in one generic way.
*/
-public class ConvertorManager {
+public class ConvertorManager implements ConvertorExecutor, ConvertorRegistrator {
private static final Logger LOG = LoggerFactory.getLogger(ConvertorManager.class);
- private static ConvertorManager INSTANCE;
-
- static {
- INSTANCE = new ConvertorManager();
- // All convertors are registered here
- INSTANCE.registerConvertor(new TableFeaturesConvertor());
- }
-
- // Actual convertor keys
- private List<Class<?>> convertorKeys = new ArrayList<>();
- private List<Class<?>> parametrizedConvertorKeys = new ArrayList<>();
// Cache, that holds all registered convertors, but they can have multiple keys,
// based on instanceof checks in the convert method
- private Map<Class<?>, Convertor> convertors = new HashMap<>();
- private Map<Class<?>, ParametrizedConvertor> parametrizedConvertors = new HashMap<>();
-
- private ConvertorManager() {
- // Hiding implicit constructor
- }
+ private Map<Short, Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>>> convertors;
/**
- * Gets instance of Convertor Manager.
+ * Create new instance of Convertor Manager.
*
- * @return the instance
+ * @param supportedVersions supported versions
*/
- public static ConvertorManager getInstance() {
- return INSTANCE;
- }
-
- /**
- * Register convertor.
- *
- * @param convertor the convertor
- * @return if registration was successful
- */
- public boolean registerConvertor(final Convertor convertor) {
- final Class<?> type = convertor.getType();
-
- if (convertors.containsKey(type)) {
- LOG.warn("Convertor for type {} is already registered", type);
- return false;
+ public ConvertorManager(final Short... supportedVersions) {
+ final Stream<Short> stream = Arrays.stream(supportedVersions);
+
+ if (supportedVersions.length == 1) {
+ final Optional<Short> versionOptional = stream.findFirst();
+ versionOptional.ifPresent(version -> convertors =
+ Collections.singletonMap(version, new ConcurrentHashMap<>()));
+ } else {
+ convertors = new ConcurrentHashMap<>();
+ stream.forEach(version -> convertors.putIfAbsent(version, new ConcurrentHashMap<>()));
}
-
- convertorKeys.add(type);
- convertors.put(type, convertor);
- LOG.debug("{} is now converted by {}", type, convertor);
- return true;
}
- /**
- * Register convertor.
- *
- * @param convertor the convertor
- * @return if registration was successful
- */
- public boolean registerConvertor(final ParametrizedConvertor convertor) {
- final Class<?> type = convertor.getType();
-
- if (parametrizedConvertors.containsKey(type)) {
- LOG.warn("Convertor for type {} is already registered", type);
- return false;
+ @Override
+ public ConvertorManager registerConvertor(final short version,
+ final Convertor<?, ?, ? extends ConvertorData> convertor) {
+ final Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
+ convertors.get(version);
+
+ if (convertorsForVersion != null) {
+ for (final Class<?> type : convertor.getTypes()) {
+ final Convertor<?, ?, ? extends ConvertorData> result = convertorsForVersion.get(type);
+
+ if (result == null) {
+ convertor.setConvertorExecutor(this);
+ convertorsForVersion.put(type, convertor);
+ LOG.debug("{} for version {} is now converted by {}", type, version, convertor);
+ } else {
+ LOG.warn("{} for version {} have already registered convertor", type, version);
+ }
+ }
+ } else {
+ LOG.warn("{} do not supports version {}", this, version);
}
- parametrizedConvertorKeys.add(type);
- parametrizedConvertors.put(type, convertor);
- LOG.debug("{} is now converted by {}", type, convertor);
- return true;
+ return this;
}
- /**
- * Lookup and use convertor by specified type, then converts source and returns converted result
- *
- * @param <FROM> the source type
- * @param <TO> the result type
- * @param source the source
- * @return the result (can be empty, if no convertor was found)
- */
+ @Override
@SuppressWarnings("unchecked")
- public <FROM, TO> Optional<TO> convert(final FROM source) {
- if (Objects.isNull(source)) {
- LOG.trace("Cannot convert source, because it is null");
- return Optional.empty();
+ public <F, T, D extends ConvertorData> Optional<T> convert(final F source, final D data) {
+ Optional<T> result = Optional.empty();
+
+ if (source == null) {
+ LOG.trace("Cannot extract type from null source");
+ return result;
}
- Class<?> type = source.getClass();
+ final Class<?> type = source instanceof DataContainer ? ((DataContainer) source).implementedInterface()
+ : source.getClass();
- if (source instanceof Collection) {
- final Iterator it = ((Collection) source).iterator();
- Object next = null;
+ if (type == null) {
+ LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
+ return result;
+ }
- if (it.hasNext()) {
- next = it.next();
- }
+ return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source, data));
+ }
- if (Objects.isNull(next)) {
- LOG.trace("Cannot convert {}, because it is empty collection", type);
- return Optional.empty();
- }
+ @Override
+ @SuppressWarnings("unchecked")
+ public <K, F, T, D extends ConvertorData> Optional<T> convert(final Map<K, F> source, final D data) {
+ Optional<T> result = Optional.empty();
- type = next.getClass();
+ if (source == null) {
+ LOG.trace("Cannot extract type from null source");
+ return result;
}
- Convertor convertor = null;
+ final Optional<F> firstOptional = source.values().stream().findFirst();
- if (!convertors.containsKey(type)) {
- boolean found = false;
+ if (!firstOptional.isPresent()) {
+ LOG.trace("Cannot extract type from empty collection");
+ return result;
+ }
- for (Class<?> key : convertorKeys) {
- if (key.isAssignableFrom(type)) {
- convertor = convertors.get(key);
- convertors.put(type, convertor);
- LOG.debug("{} is now converted by {}", type, convertor);
- found = true;
- break;
- }
- }
+ final F first = firstOptional.get();
- if (!found) {
- LOG.error("Convertor for {} not found", type);
- return Optional.empty();
- }
- }
+ final Class<?> type = first instanceof DataContainer ? ((DataContainer) first).implementedInterface()
+ : first.getClass();
- if (Objects.isNull(convertor)) {
- convertor = convertors.get(type);
+ if (type == null) {
+ LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
+ return result;
}
- final Object result = convertor.convert(source);
- return Optional.of((TO) result);
+ return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source.values(), data));
}
- /**
- * Lookup and use convertor by specified type, then converts source and returns converted result
- *
- * @param <FROM> the source type
- * @param <TO> the result type
- * @param <DATA> the data type
- * @param source the source
- * @param data convertor data
- * @return the result (can be empty, if no convertor was found)
- */
+ @Override
@SuppressWarnings("unchecked")
- public <FROM, TO, DATA extends ConvertorData> Optional<TO> convert(final FROM source, final DATA data) {
- if (Objects.isNull(source)) {
- LOG.trace("Cannot convert source, because it is null");
- return Optional.empty();
+ public <F, T, D extends ConvertorData> Optional<T> convert(final Collection<F> source, final D data) {
+ Optional<T> result = Optional.empty();
+
+ if (source == null) {
+ LOG.trace("Cannot extract type from null source");
+ return result;
}
- Class<?> type = source.getClass();
+ final Optional<F> firstOptional = source.stream().findFirst();
- if (source instanceof Collection) {
- final Iterator it = ((Collection) source).iterator();
- Object next = null;
+ if (!firstOptional.isPresent()) {
+ LOG.trace("Cannot extract type from empty collection");
+ return result;
+ }
- if (it.hasNext()) {
- next = it.next();
- }
+ final F first = firstOptional.get();
- if (Objects.isNull(next)) {
- LOG.trace("Cannot convert {}, because it is empty collection", type);
- return Optional.empty();
- }
+ final Class<?> type = first instanceof DataContainer ? ((DataContainer) first).implementedInterface()
+ : first.getClass();
- type = next.getClass();
+ if (type == null) {
+ LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
+ return result;
}
- ParametrizedConvertor convertor = null;
-
- if (!parametrizedConvertors.containsKey(type)) {
- boolean found = false;
+ return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source, data));
+ }
- for (Class<?> key : parametrizedConvertorKeys) {
- if (key.isAssignableFrom(type)) {
- convertor = parametrizedConvertors.get(key);
- parametrizedConvertors.put(type, convertor);
- LOG.debug("{} is now converted by {}", type, convertor);
- found = true;
- break;
+ /**
+ * Last resort. If we do not already have convertor registered,
+ * we will perform some costly operations and try to find if we
+ * can convert input using any of already registered convertors
+ * @param type input type
+ * @return found convertor
+ */
+ @VisibleForTesting
+ Optional<Convertor> findConvertor(final short version, final Class<?> type) {
+ final Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
+ convertors.get(version);
+
+ Optional<Convertor> convertor = Optional.empty();
+
+ if (convertorsForVersion != null) {
+ convertor = Optional.ofNullable(convertorsForVersion.get(type));
+
+ if (!convertor.isPresent()) {
+ for (Entry<Class<?>, Convertor<?, ?, ? extends ConvertorData>> entry :
+ convertorsForVersion.entrySet()) {
+ final Class<?> convertorType = entry.getKey();
+ if (type.isAssignableFrom(convertorType)) {
+ final Convertor<?, ?, ? extends ConvertorData> foundConvertor = entry.getValue();
+ convertor = Optional.ofNullable(foundConvertor);
+
+ if (convertor.isPresent()) {
+ convertorsForVersion.put(type, foundConvertor);
+ LOG.warn("{} for version {} is now converted by {} using last resort method",
+ type, version, foundConvertor);
+ break;
+ }
+ }
}
- }
- if (!found) {
- LOG.error("Convertor for {} not found", type);
- return Optional.empty();
+ if (!convertor.isPresent()) {
+ LOG.warn("Convertor for {} for version {} not found", type, version);
+ }
}
+ } else {
+ LOG.warn("{} do not supports version {}", this, version);
}
- if (Objects.isNull(convertor)) {
- convertor = parametrizedConvertors.get(type);
- }
-
- final Object result = convertor.convert(source, data);
- return Optional.of((TO) result);
+ return convertor;
}
-}
\ No newline at end of file
+}