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