91b0a1d08c4d32c5b84d54167eb798e764ca0890
[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
47 import org.opendaylight.yangtools.yang.binding.*;
48 import org.osgi.framework.Bundle;
49 import org.osgi.framework.BundleContext;
50 import org.osgi.framework.FrameworkUtil;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 public class YangToolsMapperHelper {
55
56     private static final Logger LOG = LoggerFactory.getLogger(YangToolsMapperHelper.class);
57     private static final String TYPEOBJECT_INSTANCE_METHOD = "getDefaultInstance";
58     private static final String BUILDER = "Builder";
59     private static final DateTimeFormatter formatterOutput =
60             DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.S'Z'").withZone(ZoneOffset.UTC);
61
62     private static BundleContext context = getBundleContext();
63     private static ConcurrentHashMap<String, Class<?>> cache = new ConcurrentHashMap<>();
64
65     private YangToolsMapperHelper() {
66         //Make unaccessible
67     }
68
69     public static Class<?> findClass(String name) throws ClassNotFoundException {
70
71         //Try first in cache
72         Class<?> res = cache.get(name);
73         if (res != null) {
74             return res;
75         }
76         //Try first in actual bundle
77         try {
78             return loadClass(null, name);
79         } catch (ClassNotFoundException e) {
80             // No problem, this bundle doesn't have the class
81         }
82         // Try to find in other bundles
83         if (context != null) {
84             //OSGi environment
85             for (Bundle b : context.getBundles()) {
86                 try {
87                     return loadClass(b, name);
88                 } catch (ClassNotFoundException e) {
89                     // No problem, this bundle doesn't have the class
90                 }
91             }
92         }
93         // really not found in any bundle
94         throw new ClassNotFoundException("Can not find class '" + name + "'");
95     }
96
97     private static Class<?> loadClass(Bundle b, String name) throws ClassNotFoundException {
98         Class<?> res = b == null ? Class.forName(name) : b.loadClass(name);
99         cache.put(name, res);
100         return res;
101     }
102
103     /**
104      * Verify if builder is available
105      *
106      * @throws ClassNotFoundException
107      **/
108     public static Class<?> assertBuilderClass(Class<?> clazz) throws ClassNotFoundException {
109         return getBuilderClass(getBuilderClassName(clazz));
110     }
111
112     public static Class<?> getBuilderClass(String name) throws ClassNotFoundException {
113         return findClass(name);
114     }
115
116     public static Class<?> getBuilderClass(Class<?> clazz) throws ClassNotFoundException {
117         return findClass(getBuilderClassName(clazz));
118     }
119
120     /**
121      * Create name of builder class
122      *
123      * @param <T>
124      * @param clazz
125      * @return builders class name
126      * @throws ClassNotFoundException
127      */
128     public static String getBuilderClassName(Class<?> clazz) {
129         return clazz.getName() + BUILDER;
130     }
131
132     @SuppressWarnings("unchecked")
133     public static Class<?> findBuilderClass(DeserializationContext ctxt, Class<?> clazz)
134             throws ClassNotFoundException {
135         return findClass(getBuilderClassName(clazz));
136     }
137
138     public static Optional<Class<?>> findBuilderClassOptional(DeserializationContext ctxt,
139             Class<?> clazz) {
140         try {
141             return Optional.of(findBuilderClass(ctxt, clazz));
142         } catch (ClassNotFoundException e) {
143             return Optional.empty();
144         }
145     }
146
147     public static <T extends BaseIdentity, S extends T> S getIdentityValueFromClass(Class<S> clazz) {
148         try {
149             Field valueField = clazz.getDeclaredField("VALUE");
150             return (S) valueField.get(clazz);
151         } catch (NoSuchFieldException | IllegalAccessException ignore) {
152         }
153         return null;
154     }
155
156     public static boolean hasClassDeclaredMethod(Class<?> clazz, String name) {
157         Method[] methods = clazz.getDeclaredMethods();
158         for (Method m : methods) {
159             if (m.getName().equals(name)) {
160                 return true;
161             }
162         }
163         return false;
164     }
165
166     @SuppressWarnings("unchecked")
167     public static <T> Optional<T> getInstanceByConstructor(Class<?> clazz, String arg)
168             throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
169             NoSuchMethodException, SecurityException {
170         List<Class<?>> ctypes = getConstructorParameterTypes(clazz, String.class);
171         Optional<Object> oObj;
172         for (Class<?> ctype : ctypes) {
173             if (ctype.equals(String.class)) {
174                 return Optional.of((T) clazz.getConstructor(ctype).newInstance(arg));
175             } else if ((oObj = getDefaultInstance(ctype, arg)).isPresent()) {
176                 return Optional.of((T) clazz.getConstructor(ctype).newInstance(oObj.get()));
177             } else {
178                 // TODO: recursive instantiation down to string constructor or
179                 // getDefaultInstance method
180                 LOG.debug("Not implemented arg:'{}' class:'{}'", arg, clazz);
181             }
182         }
183         return Optional.empty();
184     }
185
186     @SuppressWarnings("unchecked")
187     public static <T> Optional<T> getDefaultInstance(@Nullable Class<?> clazz, String arg) throws NoSuchMethodException,
188             SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
189         LOG.trace("arg:'{}' clazz '{}'", arg, clazz != null ? clazz.getName() : "null");
190         if (clazz != null) {
191             Method[] methods = clazz.getDeclaredMethods();
192             for (Method m : methods) {
193                 //TODO Verify argument type to avoid exception
194                 if (m.getName().equals(TYPEOBJECT_INSTANCE_METHOD)) {
195                     Method method = clazz.getDeclaredMethod(TYPEOBJECT_INSTANCE_METHOD, String.class);
196                     LOG.trace("Invoke {} available {}", TYPEOBJECT_INSTANCE_METHOD, method != null);
197                     return Optional.of((T) method.invoke(null, arg));
198                 }
199             }
200         }
201         return Optional.empty();
202     }
203
204     public static <T> Optional<T> getDefaultInstance(Optional<Class<T>> optionalClazz, String arg)
205             throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
206             InvocationTargetException {
207         if (optionalClazz.isPresent()) {
208             return getDefaultInstance(optionalClazz.get(), arg);
209         }
210         return Optional.empty();
211     }
212
213     public static List<Class<?>> getConstructorParameterTypes(Class<?> clazz, Class<?> prefer) {
214
215         Constructor<?>[] constructors = clazz.getConstructors();
216         List<Class<?>> res = new ArrayList<>();
217         for (Constructor<?> c : constructors) {
218             Class<?>[] ptypes = c.getParameterTypes();
219             if (ptypes.length == 1) {
220                 res.add(ptypes[0]);
221             }
222
223             if (prefer != null && ptypes.length == 1 && ptypes[0].equals(prefer)) {
224                 return Arrays.asList(prefer);
225             }
226         }
227         return res;
228     }
229
230     public static boolean implementsInterface(Class<?> clz, Class<?> ifToImplement) {
231         if (clz.equals(ifToImplement)) {
232             return true;
233         }
234         Class<?>[] ifs = clz.getInterfaces();
235         for (Class<?> iff : ifs) {
236             if (iff.equals(ifToImplement)) {
237                 return true;
238             }
239         }
240         return ifToImplement.isAssignableFrom(clz);
241     }
242
243     /**
244      * Provide mapping of string to attribute names, generated by yang-tools. "netconf-id" converted to "_netconfId"
245      *
246      * @param name with attribute name, not null or empty
247      * @return converted string or null if name was empty or null
248      */
249     public @Nullable
250     static String toCamelCaseAttributeName(final String name) {
251         if (name == null || name.isEmpty())
252             return null;
253
254         final StringBuilder ret = new StringBuilder(name.length());
255         if (!name.startsWith("_"))
256             ret.append('_');
257         ret.append(toCamelCase(name));
258         return ret.toString();
259     }
260
261     public static String toCamelCase(final String name) {
262         int start = 0;
263         final StringBuilder ret = new StringBuilder(name.length());
264         for (final String word : name.split("-")) {
265             if (!word.isEmpty()) {
266                 if (start++ == 0) {
267                     ret.append(Character.toLowerCase(word.charAt(0)));
268                 } else {
269                     ret.append(Character.toUpperCase(word.charAt(0)));
270                 }
271                 ret.append(word.substring(1));
272             }
273         }
274         return ret.toString();
275     }
276
277     public static String toCamelCaseClassName(final String name) {
278         final String clsName = toCamelCase(name);
279         return clsName.substring(0, 1).toUpperCase() + clsName.substring(1);
280     }
281
282     private static BundleContext getBundleContext() {
283         Bundle bundle = FrameworkUtil.getBundle(YangToolsMapperHelper.class);
284         return bundle != null ? bundle.getBundleContext() : null;
285     }
286
287     public static boolean hasTime(Notification notification) {
288         return notification instanceof EventInstantAware;
289     }
290
291     public static boolean hasTime(DOMNotification notification) {
292         return notification instanceof DOMEvent;
293     }
294
295     public static DateAndTime getTime(Notification notification, Instant defaultValue) {
296         Instant time;
297         if (hasTime(notification)) { // If notification class extends/implements the EventInstantAware
298             time = ((EventInstantAware) notification).eventInstant();
299             LOG.debug("Event time {}", time);
300         } else {
301             time = defaultValue;
302             LOG.debug("Defaulting to actual time of processing the notification - {}", time);
303         }
304         return DateAndTime.getDefaultInstance(ZonedDateTime.ofInstant(time, ZoneOffset.UTC).format(formatterOutput));
305     }
306
307     public static DateAndTime getTime(DOMNotification notification, Instant defaultValue) {
308         Instant time;
309         if (hasTime(notification)) { // If notification class extends/implements the EventInstantAware
310             time = ((DOMEvent) notification).getEventInstant();
311             LOG.debug("Event time {}", time);
312         } else {
313             time = defaultValue;
314             LOG.debug("Defaulting to actual time of processing the notification - {}", time);
315         }
316         return DateAndTime.getDefaultInstance(ZonedDateTime.ofInstant(time, ZoneOffset.UTC).format(formatterOutput));
317     }
318
319
320     public static <K extends Key<V>, V extends KeyAware<K>> Map<K, V> toMap(List<V> list) {
321         return list == null || list.isEmpty() ? null : Maps.uniqueIndex(list, KeyAware::key);
322     }
323
324     @SuppressWarnings("unchecked")
325     public static <S, T> T callBuild(S builder)
326             throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
327             InvocationTargetException {
328         Method method = builder.getClass().getMethod("build");
329         return (T) method.invoke(builder);
330     }
331 }