Merge "Drop the odlparent.netty property"
[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
9 package org.opendaylight.openflowplugin.openflow.md.core.sal.convertor;
10
11 import com.google.common.annotations.VisibleForTesting;
12 import java.util.Arrays;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.Map;
16 import java.util.Objects;
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.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * Manages various convertors and allows to use them all in one generic way
28  */
29 public class ConvertorManager implements ConvertorExecutor, ConvertorRegistrator {
30     private static final Logger LOG = LoggerFactory.getLogger(ConvertorManager.class);
31
32     // Cache, that holds all registered convertors, but they can have multiple keys,
33     // based on instanceof checks in the convert method
34     private Map<Short, Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>>> convertors;
35
36     /**
37      * Create new instance of Convertor Manager
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 = Collections.singletonMap(version, new ConcurrentHashMap<>()));
46         } else {
47             convertors = new ConcurrentHashMap<>();
48             stream.forEach(version -> convertors.putIfAbsent(version, new ConcurrentHashMap<>()));
49         }
50     }
51
52     @Override
53     public ConvertorManager registerConvertor(final short version, final Convertor<?, ?, ? extends ConvertorData> convertor) {
54         final Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
55                 convertors.get(version);
56
57         if (Objects.nonNull(convertorsForVersion)) {
58             for (final Class<?> type : convertor.getTypes()) {
59                 final Convertor<?, ?, ? extends ConvertorData> result = convertorsForVersion.get(type);
60
61                 if (Objects.isNull(result)) {
62                     convertor.setConvertorExecutor(this);
63                     convertorsForVersion.put(type, convertor);
64                     LOG.debug("{} for version {} is now converted by {}", type, version, convertor);
65                 } else {
66                     LOG.warn("{} for version {} have already registered convertor", type, version);
67                 }
68             }
69         } else {
70             LOG.warn("{} do not supports version {}", this, version);
71         }
72
73         return this;
74     }
75
76     @Override
77     @SuppressWarnings("unchecked")
78     public <FROM, TO, DATA extends ConvertorData> Optional<TO> convert(final FROM source, final DATA data) {
79         Optional<TO> result = Optional.empty();
80
81         if (Objects.isNull(source)) {
82             LOG.trace("Cannot extract type from null source");
83             return result;
84         }
85
86         final Class<?> type = source instanceof DataContainer ?
87                 ((DataContainer)source).getImplementedInterface()
88                 : source.getClass();
89
90         if (Objects.isNull(type)) {
91             LOG.warn("Cannot extract type from {}, because getImplementedInterface() returns null", source);
92             return result;
93         }
94
95          return findConvertor(data.getVersion(), type)
96                 .map(convertor -> (TO)convertor.convert(source, data));
97     }
98
99     @Override
100     @SuppressWarnings("unchecked")
101     public <FROM, TO, DATA extends ConvertorData> Optional<TO> convert(final Collection<FROM> source, final DATA data) {
102         Optional<TO> result = Optional.empty();
103
104         if (Objects.isNull(source)) {
105             LOG.trace("Cannot extract type from null source");
106             return result;
107         }
108
109         final Optional<FROM> 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 FROM first = firstOptional.get();
117
118         final Class<?> type = first instanceof DataContainer ?
119                 ((DataContainer)first).getImplementedInterface()
120                 : first.getClass();
121
122         if (Objects.isNull(type)) {
123             LOG.warn("Cannot extract type from {}, because getImplementedInterface() returns null", source);
124             return result;
125         }
126
127         return findConvertor(data.getVersion(), type)
128                 .map(convertor -> (TO)convertor.convert(source, data));
129     }
130
131     /**
132      * Last resort. If we do not already have convertor registered,
133      * we will perform some costly operations and try to find if we
134      * can convert input using any of already registered convertors
135      * @param type input type
136      * @return found convertor
137      */
138     @VisibleForTesting
139     Optional<Convertor> findConvertor(final short version, final Class<?> type) {
140         final Map<Class<?>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
141                 convertors.get(version);
142
143         Optional<Convertor> convertor = Optional.empty();
144
145         if (Objects.nonNull(convertorsForVersion)) {
146             convertor = Optional.ofNullable(convertorsForVersion.get(type));
147
148             if (!convertor.isPresent()) {
149                 for (final Class<?> convertorType : convertorsForVersion.keySet()) {
150                     if (type.isAssignableFrom(convertorType)) {
151                         final Convertor<?, ?, ? extends ConvertorData> foundConvertor = convertorsForVersion.get(convertorType);
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", type, version, foundConvertor);
157                             break;
158                         }
159                     }
160                 }
161
162                 if (!convertor.isPresent()) {
163                     LOG.warn("Convertor for {} for version {} not found", type, version);
164                 }
165             }
166         } else {
167             LOG.warn("{} do not supports version {}", this, version);
168         }
169
170         return convertor;
171     }
172 }