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