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