Update MRI projects for Aluminium
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / sal / convertor / ConvertorManager.java
index b9981b46fbc3cce7da006f6af354177252ab8556..42c6af9f7b0b09e50a14afcff005a6a508d33fe4 100644 (file)
  * 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.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 org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.action.ActionConvertor;
-import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.action.ActionResponseConvertor;
+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.openflowplugin.openflow.md.core.sal.convertor.flow.FlowConvertor;
-import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.FlowInstructionResponseConvertor;
-import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.flow.FlowStatsResponseConvertor;
-import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchResponseConvertor;
-import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchV10ResponseConvertor;
+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 implements ConvertorRegistrator, ConvertorExecutor {
+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());
-        INSTANCE.registerConvertor(new TableFeaturesResponseConvertor());
-        INSTANCE.registerConvertor(new MeterConvertor());
-        INSTANCE.registerConvertor(new MeterStatsResponseConvertor());
-        INSTANCE.registerConvertor(new MeterConfigStatsResponseConvertor());
-        INSTANCE.registerConvertor(new PortConvertor());
-        // TODO: Add MatchConvertor
-        INSTANCE.registerConvertor(new MatchResponseConvertor());
-        INSTANCE.registerConvertor(new MatchV10ResponseConvertor());
-        INSTANCE.registerConvertor(new ActionConvertor());
-        INSTANCE.registerConvertor(new ActionResponseConvertor());
-        INSTANCE.registerConvertor(new GroupConvertor());
-        INSTANCE.registerConvertor(new GroupDescStatsResponseConvertor());
-        INSTANCE.registerConvertor(new GroupStatsResponseConvertor());
-        INSTANCE.registerConvertor(new PacketOutConvertor());
-        INSTANCE.registerConvertor(new FlowConvertor());
-        INSTANCE.registerConvertor(new FlowInstructionResponseConvertor());
-        INSTANCE.registerConvertor(new FlowStatsResponseConvertor());
-    }
-
-    // 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 ConcurrentHashMap<>();
-    private Map<Class<?>, ParametrizedConvertor> parametrizedConvertors = new ConcurrentHashMap<>();
-
-    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;
+    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<>()));
+        }
     }
 
     @Override
-    public Convertor registerConvertor(final Convertor convertor) {
-        final Class<?> type = convertor.getType();
-        final Convertor result = convertors.get(type);
-
-        if (Objects.isNull(result)) {
-            convertorKeys.add(type);
-            convertors.put(type, convertor);
-            LOG.debug("{} is now converted by {}", type, convertor);
+    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("Convertor for type {} is already registered", type);
+            LOG.warn("{} do not supports version {}", this, version);
         }
 
-        return result;
+        return this;
     }
 
     @Override
-    public ParametrizedConvertor registerConvertor(final ParametrizedConvertor convertor) {
-        final Class<?> type = convertor.getType();
-        final ParametrizedConvertor result = parametrizedConvertors.get(type);
-
-        if (Objects.isNull(result)) {
-            parametrizedConvertorKeys.add(type);
-            parametrizedConvertors.put(type, convertor);
-            LOG.debug("{} is now converted by {}", type, convertor);
-        } else {
-            LOG.warn("Convertor for type {} is already registered", type);
+    @SuppressWarnings("unchecked")
+    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;
         }
 
-        return result;
+        final Class<?> type = source instanceof DataContainer ? ((DataContainer) source).implementedInterface()
+                : source.getClass();
+
+        if (type == null) {
+            LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
+            return result;
+        }
+
+        return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source, data));
     }
 
     @Override
     @SuppressWarnings("unchecked")
-    public <FROM, TO> Optional<TO> convert(final FROM source) {
-        final Optional<Class<?>> optionalType = extractType(source);
+    public <K, F, T, D extends ConvertorData> Optional<T> convert(final Map<K, F> source, final D data) {
+        Optional<T> result = Optional.empty();
 
-        if (!optionalType.isPresent()) {
-            LOG.trace("Cannot convert {}", source);
-            return Optional.empty();
+        if (source == null) {
+            LOG.trace("Cannot extract type from null source");
+            return result;
         }
 
-        final Class<?> type = optionalType.get();
-        Convertor convertor = convertors.get(type);
+        final Optional<F> firstOptional = source.values().stream().findFirst();
 
-        if (Objects.isNull(convertor)) {
-            for (final Class<?> key : convertorKeys) {
-                if (key.isAssignableFrom(type)) {
-                    convertor = convertors.get(key);
-                    convertors.put(type, convertor);
-                    LOG.debug("{} is now converted by {}", type, convertor);
-                    break;
-                }
-            }
+        if (!firstOptional.isPresent()) {
+            LOG.trace("Cannot extract type from empty collection");
+            return result;
+        }
 
-            if (Objects.isNull(convertor)) {
-                LOG.warn("Convertor for {} not found", type);
-                return Optional.empty();
-            }
+        final F first = firstOptional.get();
+
+        final Class<?> type = first instanceof DataContainer ? ((DataContainer) first).implementedInterface()
+                : first.getClass();
+
+        if (type == null) {
+            LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
+            return result;
         }
 
-        return Optional.of((TO) convertor.convert(source));
+        return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source.values(), data));
     }
 
     @Override
     @SuppressWarnings("unchecked")
-    public <FROM, TO, DATA extends ConvertorData> Optional<TO> convert(final FROM source, final DATA data) {
-        final Optional<Class<?>> optionalType = extractType(source);
+    public <F, T, D extends ConvertorData> Optional<T> convert(final Collection<F> source, final D data) {
+        Optional<T> result = Optional.empty();
 
-        if (!optionalType.isPresent()) {
-            LOG.trace("Cannot convert {}", source);
-            return Optional.empty();
+        if (source == null) {
+            LOG.trace("Cannot extract type from null source");
+            return result;
         }
 
-        final Class<?> type = optionalType.get();
-        ParametrizedConvertor convertor = parametrizedConvertors.get(type);
+        final Optional<F> firstOptional = source.stream().findFirst();
 
-        if (Objects.isNull(convertor)) {
-            for (final Class<?> key : parametrizedConvertorKeys) {
-                if (key.isAssignableFrom(type)) {
-                    convertor = parametrizedConvertors.get(key);
-                    parametrizedConvertors.put(type, convertor);
-                    LOG.debug("{} is now converted by {}", type, convertor);
-                    break;
-                }
-            }
-
-            if (Objects.isNull(convertor)) {
-                LOG.warn("Convertor for {} not found", type);
-                return Optional.empty();
-            }
+        if (!firstOptional.isPresent()) {
+            LOG.trace("Cannot extract type from empty collection");
+            return result;
         }
 
-        return Optional.of((TO) convertor.convert(source, data));
-    }
+        final F first = firstOptional.get();
 
-    private <FROM> Optional<Class<?>> extractType(FROM source) {
-        if (Objects.isNull(source)) {
-            LOG.trace("Cannot extract type from source, because it is null");
-            return Optional.empty();
+        final Class<?> type = first instanceof DataContainer ? ((DataContainer) first).implementedInterface()
+                : first.getClass();
+
+        if (type == null) {
+            LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
+            return result;
         }
 
-        Class<?> type = source.getClass();
+        return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source, data));
+    }
 
-        if (source instanceof Collection) {
-            final Optional first = ((Collection) source).stream().findFirst();
+    /**
+     * 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 (!first.isPresent()) {
-                LOG.trace("Cannot extract type {}, because it is empty collection", type);
-                return Optional.empty();
+                if (!convertor.isPresent()) {
+                    LOG.warn("Convertor for {} for version {} not found", type, version);
+                }
             }
-
-            type = first.get().getClass();
+        } else {
+            LOG.warn("{} do not supports version {}", this, version);
         }
 
-        return Optional.of(type);
+        return convertor;
     }
-}
\ No newline at end of file
+}