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