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