Merge "Device*Registry quickfix - add failed"
[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<? extends DataContainer>, 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<? extends DataContainer>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
55                 convertors.get(version);
56
57         if (Objects.nonNull(convertorsForVersion)) {
58             for (final Class<? extends DataContainer> 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 extends DataContainer, 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<? extends DataContainer> type = source.getImplementedInterface();
87
88         if (Objects.isNull(type)) {
89             LOG.warn("Cannot extract type from {}, because getImplementedInterface() returns null", source);
90             return result;
91         }
92
93          return findConvertor(data.getVersion(), type)
94                 .map(convertor -> (TO)convertor.convert(source, data));
95     }
96
97     @Override
98     @SuppressWarnings("unchecked")
99     public <FROM extends DataContainer, TO, DATA extends ConvertorData> Optional<TO> convert(final Collection<FROM> source, final DATA data) {
100         Optional<TO> result = Optional.empty();
101
102         if (Objects.isNull(source)) {
103             LOG.trace("Cannot extract type from null source");
104             return result;
105         }
106
107         final Optional<FROM> first = source.stream().findFirst();
108
109         if (!first.isPresent()) {
110             LOG.trace("Cannot extract type from empty collection");
111             return result;
112         }
113
114         final Class<? extends DataContainer> type = first.get().getImplementedInterface();
115
116         if (Objects.isNull(type)) {
117             LOG.warn("Cannot extract type from {}, because getImplementedInterface() returns null", source);
118             return result;
119         }
120
121         return findConvertor(data.getVersion(), type)
122                 .map(convertor -> (TO)convertor.convert(source, data));
123     }
124
125     /**
126      * Last resort. If we do not already have convertor registered,
127      * we will perform some costly operations and try to find if we
128      * can convert input using any of already registered convertors
129      * @param type input type
130      * @return found convertor
131      */
132     @VisibleForTesting
133     Optional<Convertor> findConvertor(final short version, final Class<? extends DataContainer> type) {
134         final Map<Class<? extends DataContainer>, Convertor<?, ?, ? extends ConvertorData>> convertorsForVersion =
135                 convertors.get(version);
136
137         Optional<Convertor> convertor = Optional.empty();
138
139         if (Objects.nonNull(convertorsForVersion)) {
140             convertor = Optional.ofNullable(convertorsForVersion.get(type));
141
142             if (!convertor.isPresent()) {
143                 for (final Class<? extends DataContainer> convertorType : convertorsForVersion.keySet()) {
144                     if (type.isAssignableFrom(convertorType)) {
145                         final Convertor<?, ?, ? extends ConvertorData> foundConvertor = convertorsForVersion.get(convertorType);
146                         convertor = Optional.ofNullable(foundConvertor);
147
148                         if (convertor.isPresent()) {
149                             convertorsForVersion.put(type, foundConvertor);
150                             LOG.warn("{} for version {} is now converted by {} using last resort method", type, version, foundConvertor);
151                             break;
152                         }
153                     }
154                 }
155
156                 if (!convertor.isPresent()) {
157                     LOG.warn("Convertor for {} for version {} not found", type, version);
158                 }
159             }
160         } else {
161             LOG.warn("{} do not supports version {}", this, version);
162         }
163
164         return convertor;
165     }
166 }