Merge "Add NodeConfiguratorImpl enqueue trace"
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / sal / convertor / ConvertorManager.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.openflowplugin.openflow.md.core.sal.convertor;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import java.util.Arrays;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.Map;
15 import java.util.Map.Entry;
16 import java.util.Optional;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.stream.Stream;
19 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.Convertor;
20 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.ConvertorData;
21 import org.opendaylight.yangtools.yang.binding.DataContainer;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * Manages various convertors and allows to use them all in one generic way.
27  */
28 public class ConvertorManager implements ConvertorExecutor, ConvertorRegistrator {
29     private static final Logger LOG = LoggerFactory.getLogger(ConvertorManager.class);
30
31     // Cache, that holds all registered convertors, but they can have multiple keys,
32     // based on instanceof checks in the convert method
33     private Map<Short, Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>>> convertors;
34
35     /**
36      * Create new instance of Convertor Manager.
37      *
38      * @param supportedVersions supported versions
39      */
40     public ConvertorManager(final Short... supportedVersions) {
41         final Stream<Short> stream = Arrays.stream(supportedVersions);
42
43         if (supportedVersions.length == 1) {
44             final Optional<Short> versionOptional = stream.findFirst();
45             versionOptional.ifPresent(version -> convertors =
46                     Collections.singletonMap(version, new ConcurrentHashMap<>()));
47         } else {
48             convertors = new ConcurrentHashMap<>();
49             stream.forEach(version -> convertors.putIfAbsent(version, new ConcurrentHashMap<>()));
50         }
51     }
52
53     @Override
54     public ConvertorManager registerConvertor(final short version,
55             final Convertor<?, ?, ? extends ConvertorData> convertor) {
56         final Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
57                 convertors.get(version);
58
59         if (convertorsForVersion != null) {
60             for (final Class<?> type : convertor.getTypes()) {
61                 final Convertor<?, ?, ? extends ConvertorData> result = convertorsForVersion.get(type);
62
63                 if (result == null) {
64                     convertor.setConvertorExecutor(this);
65                     convertorsForVersion.put(type, convertor);
66                     LOG.debug("{} for version {} is now converted by {}", type, version, convertor);
67                 } else {
68                     LOG.warn("{} for version {} have already registered convertor", type, version);
69                 }
70             }
71         } else {
72             LOG.warn("{} do not supports version {}", this, version);
73         }
74
75         return this;
76     }
77
78     @Override
79     @SuppressWarnings("unchecked")
80     public <F, T, D extends ConvertorData> Optional<T> convert(final F source, final D data) {
81         Optional<T> result = Optional.empty();
82
83         if (source == null) {
84             LOG.trace("Cannot extract type from null source");
85             return result;
86         }
87
88         final Class<?> type = source instanceof DataContainer ? ((DataContainer) source).implementedInterface()
89                 : source.getClass();
90
91         if (type == null) {
92             LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
93             return result;
94         }
95
96         return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source, data));
97     }
98
99     @Override
100     @SuppressWarnings("unchecked")
101     public <F, T, D extends ConvertorData> Optional<T> convert(final Collection<F> source, final D data) {
102         Optional<T> result = Optional.empty();
103
104         if (source == null) {
105             LOG.trace("Cannot extract type from null source");
106             return result;
107         }
108
109         final Optional<F> firstOptional = source.stream().findFirst();
110
111         if (!firstOptional.isPresent()) {
112             LOG.trace("Cannot extract type from empty collection");
113             return result;
114         }
115
116         final F first = firstOptional.get();
117
118         final Class<?> type = first instanceof DataContainer ? ((DataContainer) first).implementedInterface()
119                 : first.getClass();
120
121         if (type == null) {
122             LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
123             return result;
124         }
125
126         return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source, data));
127     }
128
129     /**
130      * Last resort. If we do not already have convertor registered,
131      * we will perform some costly operations and try to find if we
132      * can convert input using any of already registered convertors
133      * @param type input type
134      * @return found convertor
135      */
136     @VisibleForTesting
137     Optional<Convertor> findConvertor(final short version, final Class<?> type) {
138         final Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
139                 convertors.get(version);
140
141         Optional<Convertor> convertor = Optional.empty();
142
143         if (convertorsForVersion != null) {
144             convertor = Optional.ofNullable(convertorsForVersion.get(type));
145
146             if (!convertor.isPresent()) {
147                 for (Entry<Class<?>, Convertor<?, ?, ? extends ConvertorData>> entry :
148                             convertorsForVersion.entrySet()) {
149                     final Class<?> convertorType = entry.getKey();
150                     if (type.isAssignableFrom(convertorType)) {
151                         final Convertor<?, ?, ? extends ConvertorData> foundConvertor = entry.getValue();
152                         convertor = Optional.ofNullable(foundConvertor);
153
154                         if (convertor.isPresent()) {
155                             convertorsForVersion.put(type, foundConvertor);
156                             LOG.warn("{} for version {} is now converted by {} using last resort method",
157                                     type, version, foundConvertor);
158                             break;
159                         }
160                     }
161                 }
162
163                 if (!convertor.isPresent()) {
164                     LOG.warn("Convertor for {} for version {} not found", type, version);
165                 }
166             }
167         } else {
168             LOG.warn("{} do not supports version {}", this, version);
169         }
170
171         return convertor;
172     }
173 }