9ca07fe59ad09d0ef470a672984d90f921306708
[ccsdk/features.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP : ccsdk features
4  * ================================================================================
5  * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
6  * All rights reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  */
22 package org.onap.ccsdk.features.sdnr.wt.yang.mapper;
23
24 import com.fasterxml.jackson.databind.DeserializationContext;
25 import com.google.common.collect.Maps;
26
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.Field;
29 import java.lang.reflect.InvocationTargetException;
30 import java.lang.reflect.Method;
31 import java.time.Instant;
32 import java.time.ZoneOffset;
33 import java.time.ZonedDateTime;
34 import java.time.format.DateTimeFormatter;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Optional;
40 import java.util.concurrent.ConcurrentHashMap;
41 import javax.annotation.Nullable;
42
43 import org.opendaylight.mdsal.dom.api.DOMEvent;
44 import org.opendaylight.mdsal.dom.api.DOMNotification;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
46 import org.opendaylight.yangtools.yang.binding.*;
47 import org.osgi.framework.Bundle;
48 import org.osgi.framework.BundleContext;
49 import org.osgi.framework.FrameworkUtil;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 public class YangToolsMapperHelper {
54
55     private static final Logger LOG = LoggerFactory.getLogger(YangToolsMapperHelper.class);
56     private static final String TYPEOBJECT_INSTANCE_METHOD = "getDefaultInstance";
57     private static final String BUILDER = "Builder";
58     private static final DateTimeFormatter formatterOutput =
59             DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.S'Z'").withZone(ZoneOffset.UTC);
60
61     private static BundleContext context = getBundleContext();
62     private static ConcurrentHashMap<String, Class<?>> cache = new ConcurrentHashMap<>();
63
64     private YangToolsMapperHelper() {
65         //Make unaccessible
66     }
67
68     public static Class<?> findClass(String name) throws ClassNotFoundException {
69
70         //Try first in cache
71         Class<?> res = cache.get(name);
72         if (res != null) {
73             return res;
74         }
75         //Try first in actual bundle
76         try {
77             return loadClass(null, name);
78         } catch (ClassNotFoundException e) {
79             // No problem, this bundle doesn't have the class
80         }
81         // Try to find in other bundles
82         if (context != null) {
83             //OSGi environment
84             for (Bundle b : context.getBundles()) {
85                 try {
86                     return loadClass(b, name);
87                 } catch (ClassNotFoundException e) {
88                     // No problem, this bundle doesn't have the class
89                 }
90             }
91         }
92         // really not found in any bundle
93         throw new ClassNotFoundException("Can not find class '" + name + "'");
94     }
95
96     private static Class<?> loadClass(Bundle b, String name) throws ClassNotFoundException {
97         Class<?> res = b == null ? Class.forName(name) : b.loadClass(name);
98         cache.put(name, res);
99         return res;
100     }
101
102     /**
103      * Verify if builder is available
104      *
105      * @throws ClassNotFoundException
106      **/
107     public static Class<?> assertBuilderClass(Class<?> clazz) throws ClassNotFoundException {
108         return getBuilderClass(getBuilderClassName(clazz));
109     }
110
111     public static Class<?> getBuilderClass(String name) throws ClassNotFoundException {
112         return findClass(name);
113     }
114
115     public static Class<?> getBuilderClass(Class<?> clazz) throws ClassNotFoundException {
116         return findClass(getBuilderClassName(clazz));
117     }
118
119     /**
120      * Create name of builder class
121      *
122      * @param <T>
123      * @param clazz
124      * @return builders class name
125      * @throws ClassNotFoundException
126      */
127     public static String getBuilderClassName(Class<?> clazz) {
128         return clazz.getName() + BUILDER;
129     }
130
131     @SuppressWarnings("unchecked")
132     public static Class<?> findBuilderClass(DeserializationContext ctxt, Class<?> clazz)
133             throws ClassNotFoundException {
134         return findClass(getBuilderClassName(clazz));
135     }
136
137     public static Optional<Class<?>> findBuilderClassOptional(DeserializationContext ctxt,
138             Class<?> clazz) {
139         try {
140             return Optional.of(findBuilderClass(ctxt, clazz));
141         } catch (ClassNotFoundException e) {
142             return Optional.empty();
143         }
144     }
145
146     public static <T extends BaseIdentity, S extends T> S getIdentityValueFromClass(Class<S> clazz) {
147         try {
148             Field valueField = clazz.getDeclaredField("VALUE");
149             return (S) valueField.get(clazz);
150         } catch (NoSuchFieldException | IllegalAccessException ignore) {
151         }
152         return null;
153     }
154
155     public static boolean hasClassDeclaredMethod(Class<?> clazz, String name) {
156         Method[] methods = clazz.getDeclaredMethods();
157         for (Method m : methods) {
158             if (m.getName().equals(name)) {
159                 return true;
160             }
161         }
162         return false;
163     }
164
165     @SuppressWarnings("unchecked")
166     public static <T> Optional<T> getInstanceByConstructor(Class<?> clazz, String arg)
167             throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
168             NoSuchMethodException, SecurityException {
169         List<Class<?>> ctypes = getConstructorParameterTypes(clazz, String.class);
170         Optional<Object> oObj;
171         for (Class<?> ctype : ctypes) {
172             if (ctype.equals(String.class)) {
173                 return Optional.of((T) clazz.getConstructor(ctype).newInstance(arg));
174             } else if ((oObj = getDefaultInstance(ctype, arg)).isPresent()) {
175                 return Optional.of((T) clazz.getConstructor(ctype).newInstance(oObj.get()));
176             } else {
177                 // TODO: recursive instantiation down to string constructor or
178                 // getDefaultInstance method
179                 LOG.debug("Not implemented arg:'{}' class:'{}'", arg, clazz);
180             }
181         }
182         return Optional.empty();
183     }
184
185     @SuppressWarnings("unchecked")
186     public static <T> Optional<T> getDefaultInstance(@Nullable Class<?> clazz, String arg) throws NoSuchMethodException,
187             SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
188         LOG.trace("arg:'{}' clazz '{}'", arg, clazz != null ? clazz.getName() : "null");
189         if (clazz != null) {
190             Method[] methods = clazz.getDeclaredMethods();
191             for (Method m : methods) {
192                 //TODO Verify argument type to avoid exception
193                 if (m.getName().equals(TYPEOBJECT_INSTANCE_METHOD)) {
194                     Method method = clazz.getDeclaredMethod(TYPEOBJECT_INSTANCE_METHOD, String.class);
195                     LOG.trace("Invoke {} available {}", TYPEOBJECT_INSTANCE_METHOD, method != null);
196                     return Optional.of((T) method.invoke(null, arg));
197                 }
198             }
199         }
200         return Optional.empty();
201     }
202
203     public static <T> Optional<T> getDefaultInstance(Optional<Class<T>> optionalClazz, String arg)
204             throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
205             InvocationTargetException {
206         if (optionalClazz.isPresent()) {
207             return getDefaultInstance(optionalClazz.get(), arg);
208         }
209         return Optional.empty();
210     }
211
212     public static List<Class<?>> getConstructorParameterTypes(Class<?> clazz, Class<?> prefer) {
213
214         Constructor<?>[] constructors = clazz.getConstructors();
215         List<Class<?>> res = new ArrayList<>();
216         for (Constructor<?> c : constructors) {
217             Class<?>[] ptypes = c.getParameterTypes();
218             if (ptypes.length == 1) {
219                 res.add(ptypes[0]);
220             }
221
222             if (prefer != null && ptypes.length == 1 && ptypes[0].equals(prefer)) {
223                 return Arrays.asList(prefer);
224             }
225         }
226         return res;
227     }
228
229     public static boolean implementsInterface(Class<?> clz, Class<?> ifToImplement) {
230         if (clz.equals(ifToImplement)) {
231             return true;
232         }
233         Class<?>[] ifs = clz.getInterfaces();
234         for (Class<?> iff : ifs) {
235             if (iff.equals(ifToImplement)) {
236                 return true;
237             }
238         }
239         return ifToImplement.isAssignableFrom(clz);
240     }
241
242     /**
243      * Provide mapping of string to attribute names, generated by yang-tools. "netconf-id" converted to "_netconfId"
244      *
245      * @param name with attribute name, not null or empty
246      * @return converted string or null if name was empty or null
247      */
248     public @Nullable
249     static String toCamelCaseAttributeName(final String name) {
250         if (name == null || name.isEmpty())
251             return null;
252
253         final StringBuilder ret = new StringBuilder(name.length());
254         if (!name.startsWith("_"))
255             ret.append('_');
256         ret.append(toCamelCase(name));
257         return ret.toString();
258     }
259
260     public static String toCamelCase(final String name) {
261         int start = 0;
262         final StringBuilder ret = new StringBuilder(name.length());
263         for (final String word : name.split("-")) {
264             if (!word.isEmpty()) {
265                 if (start++ == 0) {
266                     ret.append(Character.toLowerCase(word.charAt(0)));
267                 } else {
268                     ret.append(Character.toUpperCase(word.charAt(0)));
269                 }
270                 ret.append(word.substring(1));
271             }
272         }
273         return ret.toString();
274     }
275
276     public static String toCamelCaseClassName(final String name) {
277         final String clsName = toCamelCase(name);
278         return clsName.substring(0, 1).toUpperCase() + clsName.substring(1);
279     }
280
281     private static BundleContext getBundleContext() {
282         Bundle bundle = FrameworkUtil.getBundle(YangToolsMapperHelper.class);
283         return bundle != null ? bundle.getBundleContext() : null;
284     }
285
286     public static boolean hasTime(Notification notification) {
287         return notification instanceof EventInstantAware;
288     }
289
290     public static boolean hasTime(DOMNotification notification) {
291         return notification instanceof DOMEvent;
292     }
293
294     public static DateAndTime getTime(Notification notification, Instant defaultValue) {
295         Instant time;
296         if (hasTime(notification)) { // If notification class extends/implements the EventInstantAware
297             time = ((EventInstantAware) notification).eventInstant();
298             LOG.debug("Event time {}", time);
299         } else {
300             time = defaultValue;
301             LOG.debug("Defaulting to actual time of processing the notification - {}", time);
302         }
303         return DateAndTime.getDefaultInstance(ZonedDateTime.ofInstant(time, ZoneOffset.UTC).format(formatterOutput));
304     }
305
306     public static DateAndTime getTime(DOMNotification notification, Instant defaultValue) {
307         Instant time;
308         if (hasTime(notification)) { // If notification class extends/implements the EventInstantAware
309             time = ((DOMEvent) notification).getEventInstant();
310             LOG.debug("Event time {}", time);
311         } else {
312             time = defaultValue;
313             LOG.debug("Defaulting to actual time of processing the notification - {}", time);
314         }
315         return DateAndTime.getDefaultInstance(ZonedDateTime.ofInstant(time, ZoneOffset.UTC).format(formatterOutput));
316     }
317
318
319     public static <K extends Identifier<V>, V extends Identifiable<K>> Map<K, V> toMap(List<V> list) {
320         return list == null || list.isEmpty() ? null : Maps.uniqueIndex(list, Identifiable::key);
321     }
322
323     @SuppressWarnings("unchecked")
324     public static <S, T> T callBuild(S builder)
325             throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
326             InvocationTargetException {
327         Method method = builder.getClass().getMethod("build");
328         return (T) method.invoke(builder);
329     }
330 }