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