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