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