Bump MRI upstreams
[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 static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import java.util.Arrays;
14 import java.util.Collection;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import java.util.Optional;
18 import java.util.concurrent.ConcurrentHashMap;
19 import java.util.stream.Stream;
20 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.Convertor;
21 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.ConvertorData;
22 import org.opendaylight.yangtools.yang.binding.DataContainer;
23 import org.opendaylight.yangtools.yang.common.Uint8;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Manages various convertors and allows to use them all in one generic way.
29  */
30 public class ConvertorManager implements ConvertorExecutor, ConvertorRegistrator {
31     private static final Logger LOG = LoggerFactory.getLogger(ConvertorManager.class);
32
33     // Cache, that holds all registered convertors, but they can have multiple keys,
34     // based on instanceof checks in the convert method
35     private Map<Uint8, Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>>> convertors;
36
37     /**
38      * Create new instance of Convertor Manager.
39      *
40      * @param supportedVersions supported versions
41      */
42     public ConvertorManager(final Uint8... supportedVersions) {
43         final Stream<Uint8> stream = Arrays.stream(supportedVersions);
44
45         if (supportedVersions.length == 1) {
46             final Optional<Uint8> versionOptional = stream.findFirst();
47             versionOptional.ifPresent(version -> convertors = Map.of(version, new ConcurrentHashMap<>()));
48         } else {
49             convertors = new ConcurrentHashMap<>();
50             stream.forEach(version -> convertors.putIfAbsent(version, new ConcurrentHashMap<>()));
51         }
52     }
53
54     @Override
55     public ConvertorManager registerConvertor(final Uint8 version,
56             final Convertor<?, ?, ? extends ConvertorData> convertor) {
57         final Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
58                 convertors.get(requireNonNull(version));
59
60         if (convertorsForVersion != null) {
61             for (final Class<?> type : convertor.getTypes()) {
62                 final Convertor<?, ?, ? extends ConvertorData> result = convertorsForVersion.get(type);
63
64                 if (result == null) {
65                     convertor.setConvertorExecutor(this);
66                     convertorsForVersion.put(type, convertor);
67                     LOG.debug("{} for version {} is now converted by {}", type, version, convertor);
68                 } else {
69                     LOG.warn("{} for version {} have already registered convertor", type, version);
70                 }
71             }
72         } else {
73             LOG.warn("{} do not supports version {}", this, version);
74         }
75
76         return this;
77     }
78
79     @Override
80     @SuppressWarnings("unchecked")
81     public <F, T, D extends ConvertorData> Optional<T> convert(final F source, final D data) {
82         Optional<T> result = Optional.empty();
83
84         if (source == null) {
85             LOG.trace("Cannot extract type from null source");
86             return result;
87         }
88
89         final Class<?> type = source instanceof DataContainer ? ((DataContainer) source).implementedInterface()
90                 : source.getClass();
91
92         if (type == null) {
93             LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
94             return result;
95         }
96
97         return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source, data));
98     }
99
100     @Override
101     @SuppressWarnings("unchecked")
102     public <K, F, T, D extends ConvertorData> Optional<T> convert(final Map<K, F> source, final D data) {
103         Optional<T> result = Optional.empty();
104
105         if (source == null) {
106             LOG.trace("Cannot extract type from null source");
107             return result;
108         }
109
110         final Optional<F> firstOptional = source.values().stream().findFirst();
111
112         if (!firstOptional.isPresent()) {
113             LOG.trace("Cannot extract type from empty collection");
114             return result;
115         }
116
117         final F first = firstOptional.get();
118
119         final Class<?> type = first instanceof DataContainer ? ((DataContainer) first).implementedInterface()
120                 : first.getClass();
121
122         if (type == null) {
123             LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
124             return result;
125         }
126
127         return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source.values(), data));
128     }
129
130     @Override
131     @SuppressWarnings("unchecked")
132     public <F, T, D extends ConvertorData> Optional<T> convert(final Collection<F> source, final D data) {
133         Optional<T> result = Optional.empty();
134
135         if (source == null) {
136             LOG.trace("Cannot extract type from null source");
137             return result;
138         }
139
140         final Optional<F> firstOptional = source.stream().findFirst();
141
142         if (!firstOptional.isPresent()) {
143             LOG.trace("Cannot extract type from empty collection");
144             return result;
145         }
146
147         final F first = firstOptional.get();
148
149         final Class<?> type = first instanceof DataContainer ? ((DataContainer) first).implementedInterface()
150                 : first.getClass();
151
152         if (type == null) {
153             LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
154             return result;
155         }
156
157         return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source, data));
158     }
159
160     /**
161      * Last resort. If we do not already have convertor registered,
162      * we will perform some costly operations and try to find if we
163      * can convert input using any of already registered convertors
164      * @param type input type
165      * @return found convertor
166      */
167     @VisibleForTesting
168     Optional<Convertor> findConvertor(final Uint8 version, final Class<?> type) {
169         final Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
170                 convertors.get(requireNonNull(version));
171
172         Optional<Convertor> convertor = Optional.empty();
173
174         if (convertorsForVersion != null) {
175             convertor = Optional.ofNullable(convertorsForVersion.get(type));
176
177             if (!convertor.isPresent()) {
178                 for (Entry<Class<?>, Convertor<?, ?, ? extends ConvertorData>> entry :
179                             convertorsForVersion.entrySet()) {
180                     final Class<?> convertorType = entry.getKey();
181                     if (type.isAssignableFrom(convertorType)) {
182                         final Convertor<?, ?, ? extends ConvertorData> foundConvertor = entry.getValue();
183                         convertor = Optional.ofNullable(foundConvertor);
184
185                         if (convertor.isPresent()) {
186                             convertorsForVersion.put(type, foundConvertor);
187                             LOG.warn("{} for version {} is now converted by {} using last resort method",
188                                     type, version, foundConvertor);
189                             break;
190                         }
191                     }
192                 }
193
194                 if (!convertor.isPresent()) {
195                     LOG.warn("Convertor for {} for version {} not found", type, version);
196                 }
197             }
198         } else {
199             LOG.warn("{} do not supports version {}", this, version);
200         }
201
202         return convertor;
203     }
204 }