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