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