2 * ============LICENSE_START=======================================================
3 * ONAP : ccsdk features
4 * ================================================================================
5 * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property.
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
22 package org.onap.ccsdk.features.sdnr.wt.yang.mapper;
24 import com.fasterxml.jackson.databind.DeserializationContext;
25 import com.google.common.collect.Maps;
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;
39 import java.util.Optional;
40 import java.util.concurrent.ConcurrentHashMap;
41 import javax.annotation.Nullable;
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;
53 public class YangToolsMapperHelper {
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);
61 private static BundleContext context = getBundleContext();
62 private static ConcurrentHashMap<String, Class<?>> cache = new ConcurrentHashMap<>();
64 private YangToolsMapperHelper() {
68 public static Class<?> findClass(String name) throws ClassNotFoundException {
71 Class<?> res = cache.get(name);
75 //Try first in actual bundle
77 return loadClass(null, name);
78 } catch (ClassNotFoundException e) {
79 // No problem, this bundle doesn't have the class
81 // Try to find in other bundles
82 if (context != null) {
84 for (Bundle b : context.getBundles()) {
86 return loadClass(b, name);
87 } catch (ClassNotFoundException e) {
88 // No problem, this bundle doesn't have the class
92 // really not found in any bundle
93 throw new ClassNotFoundException("Can not find class '" + name + "'");
96 private static Class<?> loadClass(Bundle b, String name) throws ClassNotFoundException {
97 Class<?> res = b == null ? Class.forName(name) : b.loadClass(name);
103 * Verify if builder is available
105 * @throws ClassNotFoundException
107 public static Class<?> assertBuilderClass(Class<?> clazz) throws ClassNotFoundException {
108 return getBuilderClass(getBuilderClassName(clazz));
111 public static Class<?> getBuilderClass(String name) throws ClassNotFoundException {
112 return findClass(name);
115 public static Class<?> getBuilderClass(Class<?> clazz) throws ClassNotFoundException {
116 return findClass(getBuilderClassName(clazz));
120 * Create name of builder class
124 * @return builders class name
125 * @throws ClassNotFoundException
127 public static String getBuilderClassName(Class<?> clazz) {
128 return clazz.getName() + BUILDER;
131 @SuppressWarnings("unchecked")
132 public static Class<?> findBuilderClass(DeserializationContext ctxt, Class<?> clazz)
133 throws ClassNotFoundException {
134 return findClass(getBuilderClassName(clazz));
137 public static Optional<Class<?>> findBuilderClassOptional(DeserializationContext ctxt,
140 return Optional.of(findBuilderClass(ctxt, clazz));
141 } catch (ClassNotFoundException e) {
142 return Optional.empty();
146 public static <T extends BaseIdentity, S extends T> S getIdentityValueFromClass(Class<S> clazz) {
148 Field valueField = clazz.getDeclaredField("VALUE");
149 return (S) valueField.get(clazz);
150 } catch (NoSuchFieldException | IllegalAccessException ignore) {
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)) {
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()));
177 // TODO: recursive instantiation down to string constructor or
178 // getDefaultInstance method
179 LOG.debug("Not implemented arg:'{}' class:'{}'", arg, clazz);
182 return Optional.empty();
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");
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));
200 return Optional.empty();
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);
209 return Optional.empty();
212 public static List<Class<?>> getConstructorParameterTypes(Class<?> clazz, Class<?> prefer) {
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) {
222 if (prefer != null && ptypes.length == 1 && ptypes[0].equals(prefer)) {
223 return Arrays.asList(prefer);
229 public static boolean implementsInterface(Class<?> clz, Class<?> ifToImplement) {
230 if (clz.equals(ifToImplement)) {
233 Class<?>[] ifs = clz.getInterfaces();
234 for (Class<?> iff : ifs) {
235 if (iff.equals(ifToImplement)) {
239 return ifToImplement.isAssignableFrom(clz);
243 * Provide mapping of string to attribute names, generated by yang-tools. "netconf-id" converted to "_netconfId"
245 * @param name with attribute name, not null or empty
246 * @return converted string or null if name was empty or null
249 static String toCamelCaseAttributeName(final String name) {
250 if (name == null || name.isEmpty())
253 final StringBuilder ret = new StringBuilder(name.length());
254 if (!name.startsWith("_"))
256 ret.append(toCamelCase(name));
257 return ret.toString();
260 public static String toCamelCase(final String name) {
262 final StringBuilder ret = new StringBuilder(name.length());
263 for (final String word : name.split("-")) {
264 if (!word.isEmpty()) {
266 ret.append(Character.toLowerCase(word.charAt(0)));
268 ret.append(Character.toUpperCase(word.charAt(0)));
270 ret.append(word.substring(1));
273 return ret.toString();
276 public static String toCamelCaseClassName(final String name) {
277 final String clsName = toCamelCase(name);
278 return clsName.substring(0, 1).toUpperCase() + clsName.substring(1);
281 private static BundleContext getBundleContext() {
282 Bundle bundle = FrameworkUtil.getBundle(YangToolsMapperHelper.class);
283 return bundle != null ? bundle.getBundleContext() : null;
286 public static boolean hasTime(Notification notification) {
287 return notification instanceof EventInstantAware;
290 public static boolean hasTime(DOMNotification notification) {
291 return notification instanceof DOMEvent;
294 public static DateAndTime getTime(Notification notification, Instant defaultValue) {
296 if (hasTime(notification)) { // If notification class extends/implements the EventInstantAware
297 time = ((EventInstantAware) notification).eventInstant();
298 LOG.debug("Event time {}", time);
301 LOG.debug("Defaulting to actual time of processing the notification - {}", time);
303 return DateAndTime.getDefaultInstance(ZonedDateTime.ofInstant(time, ZoneOffset.UTC).format(formatterOutput));
306 public static DateAndTime getTime(DOMNotification notification, Instant defaultValue) {
308 if (hasTime(notification)) { // If notification class extends/implements the EventInstantAware
309 time = ((DOMEvent) notification).getEventInstant();
310 LOG.debug("Event time {}", time);
313 LOG.debug("Defaulting to actual time of processing the notification - {}", time);
315 return DateAndTime.getDefaultInstance(ZonedDateTime.ofInstant(time, ZoneOffset.UTC).format(formatterOutput));
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);
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);