Bug 5540 - PortConvertor, MatchConvertor
[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 java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.HashMap;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Objects;
18 import java.util.Optional;
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.openflowplugin.openflow.md.core.sal.convertor.common.ParametrizedConvertor;
22 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchResponseConvertor;
23 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchV10ResponseConvertor;
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 {
31     private static final Logger LOG = LoggerFactory.getLogger(ConvertorManager.class);
32     private static ConvertorManager INSTANCE;
33
34     static {
35         INSTANCE = new ConvertorManager();
36         // All convertors are registered here
37         INSTANCE.registerConvertor(new TableFeaturesConvertor());
38         INSTANCE.registerConvertor(new TableFeaturesResponseConvertor());
39         INSTANCE.registerConvertor(new MeterConvertor());
40         INSTANCE.registerConvertor(new MeterStatsResponseConvertor());
41         INSTANCE.registerConvertor(new MeterConfigStatsResponseConvertor());
42         INSTANCE.registerConvertor(new PortConvertor());
43         // TODO: Add MatchConvertor
44         INSTANCE.registerConvertor(new MatchResponseConvertor());
45         INSTANCE.registerConvertor(new MatchV10ResponseConvertor());
46     }
47
48     // Actual convertor keys
49     private List<Class<?>> convertorKeys = new ArrayList<>();
50     private List<Class<?>> parametrizedConvertorKeys = new ArrayList<>();
51
52     // Cache, that holds all registered convertors, but they can have multiple keys,
53     // based on instanceof checks in the convert method
54     private Map<Class<?>, Convertor> convertors = new HashMap<>();
55     private Map<Class<?>, ParametrizedConvertor> parametrizedConvertors = new HashMap<>();
56
57     private ConvertorManager() {
58         // Hiding implicit constructor
59     }
60
61     /**
62      * Gets instance of Convertor Manager.
63      *
64      * @return the instance
65      */
66     public static ConvertorManager getInstance() {
67         return INSTANCE;
68     }
69
70     /**
71      * Register convertor.
72      *
73      * @param convertor the convertor
74      * @return if registration was successful
75      */
76     public boolean registerConvertor(final Convertor convertor) {
77         final Class<?> type = convertor.getType();
78
79         if (convertors.containsKey(type)) {
80             LOG.warn("Convertor for type {} is already registered", type);
81             return false;
82         }
83
84         convertorKeys.add(type);
85         convertors.put(type, convertor);
86         LOG.debug("{} is now converted by {}", type, convertor);
87         return true;
88     }
89
90     /**
91      * Register convertor.
92      *
93      * @param convertor the convertor
94      * @return if registration was successful
95      */
96     public boolean registerConvertor(final ParametrizedConvertor convertor) {
97         final Class<?> type = convertor.getType();
98
99         if (parametrizedConvertors.containsKey(type)) {
100             LOG.warn("Convertor for type {} is already registered", type);
101             return false;
102         }
103
104         parametrizedConvertorKeys.add(type);
105         parametrizedConvertors.put(type, convertor);
106         LOG.debug("{} is now converted by {}", type, convertor);
107         return true;
108     }
109
110     /**
111      * Lookup and use convertor by specified type, then converts source and returns converted result
112      *
113      * @param <FROM> the source type
114      * @param <TO>   the result type
115      * @param source the source
116      * @return the result (can be empty, if no convertor was found)
117      */
118     @SuppressWarnings("unchecked")
119     public <FROM, TO> Optional<TO> convert(final FROM source) {
120         if (Objects.isNull(source)) {
121             LOG.trace("Cannot convert source, because it is null");
122             return Optional.empty();
123         }
124
125         Class<?> type = source.getClass();
126
127         if (source instanceof Collection) {
128             final Iterator it = ((Collection) source).iterator();
129             Object next = null;
130
131             if (it.hasNext()) {
132                 next = it.next();
133             }
134
135             if (Objects.isNull(next)) {
136                 LOG.trace("Cannot convert {}, because it is empty collection", type);
137                 return Optional.empty();
138             }
139
140             type = next.getClass();
141         }
142
143         Convertor convertor = null;
144
145         if (!convertors.containsKey(type)) {
146             boolean found = false;
147
148             for (Class<?> key : convertorKeys) {
149                 if (key.isAssignableFrom(type)) {
150                     convertor = convertors.get(key);
151                     convertors.put(type, convertor);
152                     LOG.debug("{} is now converted by {}", type, convertor);
153                     found = true;
154                     break;
155                 }
156             }
157
158             if (!found) {
159                 LOG.error("Convertor for {} not found", type);
160                 return Optional.empty();
161             }
162         }
163
164         if (Objects.isNull(convertor)) {
165             convertor = convertors.get(type);
166         }
167
168         final Object result = convertor.convert(source);
169         return Optional.of((TO) result);
170     }
171
172     /**
173      * Lookup and use convertor by specified type, then converts source and returns converted result
174      *
175      * @param <FROM> the source type
176      * @param <TO>   the result type
177      * @param <DATA> the data type
178      * @param source the source
179      * @param data   convertor data
180      * @return the result (can be empty, if no convertor was found)
181      */
182     @SuppressWarnings("unchecked")
183     public <FROM, TO, DATA extends ConvertorData> Optional<TO> convert(final FROM source, final DATA data) {
184         if (Objects.isNull(source)) {
185             LOG.trace("Cannot convert source, because it is null");
186             return Optional.empty();
187         }
188
189         Class<?> type = source.getClass();
190
191         if (source instanceof Collection) {
192             final Iterator it = ((Collection) source).iterator();
193             Object next = null;
194
195             if (it.hasNext()) {
196                 next = it.next();
197             }
198
199             if (Objects.isNull(next)) {
200                 LOG.trace("Cannot convert {}, because it is empty collection", type);
201                 return Optional.empty();
202             }
203
204             type = next.getClass();
205         }
206
207         ParametrizedConvertor convertor = null;
208
209         if (!parametrizedConvertors.containsKey(type)) {
210             boolean found = false;
211
212             for (Class<?> key : parametrizedConvertorKeys) {
213                 if (key.isAssignableFrom(type)) {
214                     convertor = parametrizedConvertors.get(key);
215                     parametrizedConvertors.put(type, convertor);
216                     LOG.debug("{} is now converted by {}", type, convertor);
217                     found = true;
218                     break;
219                 }
220             }
221
222             if (!found) {
223                 LOG.error("Convertor for {} not found", type);
224                 return Optional.empty();
225             }
226         }
227
228         if (Objects.isNull(convertor)) {
229             convertor = parametrizedConvertors.get(type);
230         }
231
232         final Object result = convertor.convert(source, data);
233         return Optional.of((TO) result);
234     }
235 }