Update MRI projects for Aluminium
[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 <K, F, T, D extends ConvertorData> Optional<T> convert(final Map<K, 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.values().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.values(), data));
127     }
128
129     @Override
130     @SuppressWarnings("unchecked")
131     public <F, T, D extends ConvertorData> Optional<T> convert(final Collection<F> source, final D data) {
132         Optional<T> result = Optional.empty();
133
134         if (source == null) {
135             LOG.trace("Cannot extract type from null source");
136             return result;
137         }
138
139         final Optional<F> firstOptional = source.stream().findFirst();
140
141         if (!firstOptional.isPresent()) {
142             LOG.trace("Cannot extract type from empty collection");
143             return result;
144         }
145
146         final F first = firstOptional.get();
147
148         final Class<?> type = first instanceof DataContainer ? ((DataContainer) first).implementedInterface()
149                 : first.getClass();
150
151         if (type == null) {
152             LOG.warn("Cannot extract type from {}, because implementedInterface() returns null", source);
153             return result;
154         }
155
156         return findConvertor(data.getVersion(), type).map(convertor -> (T)convertor.convert(source, data));
157     }
158
159     /**
160      * Last resort. If we do not already have convertor registered,
161      * we will perform some costly operations and try to find if we
162      * can convert input using any of already registered convertors
163      * @param type input type
164      * @return found convertor
165      */
166     @VisibleForTesting
167     Optional<Convertor> findConvertor(final short version, final Class<?> type) {
168         final Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
169                 convertors.get(version);
170
171         Optional<Convertor> convertor = Optional.empty();
172
173         if (convertorsForVersion != null) {
174             convertor = Optional.ofNullable(convertorsForVersion.get(type));
175
176             if (!convertor.isPresent()) {
177                 for (Entry<Class<?>, Convertor<?, ?, ? extends ConvertorData>> entry :
178                             convertorsForVersion.entrySet()) {
179                     final Class<?> convertorType = entry.getKey();
180                     if (type.isAssignableFrom(convertorType)) {
181                         final Convertor<?, ?, ? extends ConvertorData> foundConvertor = entry.getValue();
182                         convertor = Optional.ofNullable(foundConvertor);
183
184                         if (convertor.isPresent()) {
185                             convertorsForVersion.put(type, foundConvertor);
186                             LOG.warn("{} for version {} is now converted by {} using last resort method",
187                                     type, version, foundConvertor);
188                             break;
189                         }
190                     }
191                 }
192
193                 if (!convertor.isPresent()) {
194                     LOG.warn("Convertor for {} for version {} not found", type, version);
195                 }
196             }
197         } else {
198             LOG.warn("{} do not supports version {}", this, version);
199         }
200
201         return convertor;
202     }
203 }