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.InvocationTargetException;
 
  29 import java.lang.reflect.Method;
 
  30 import java.time.Instant;
 
  31 import java.time.ZoneOffset;
 
  32 import java.time.ZonedDateTime;
 
  33 import java.time.format.DateTimeFormatter;
 
  34 import java.util.ArrayList;
 
  35 import java.util.Arrays;
 
  36 import java.util.List;
 
  38 import java.util.Optional;
 
  39 import java.util.concurrent.ConcurrentHashMap;
 
  40 import javax.annotation.Nullable;
 
  41 import org.opendaylight.mdsal.dom.api.DOMEvent;
 
  42 import org.opendaylight.mdsal.dom.api.DOMNotification;
 
  43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
 
  44 import org.opendaylight.yangtools.concepts.Builder;
 
  45 import org.opendaylight.yangtools.yang.binding.EventInstantAware;
 
  46 import org.opendaylight.yangtools.yang.binding.Identifiable;
 
  47 import org.opendaylight.yangtools.yang.binding.Identifier;
 
  48 import org.opendaylight.yangtools.yang.binding.Notification;
 
  49 import org.osgi.framework.Bundle;
 
  50 import org.osgi.framework.BundleContext;
 
  51 import org.osgi.framework.FrameworkUtil;
 
  52 import org.slf4j.Logger;
 
  53 import org.slf4j.LoggerFactory;
 
  55 public class YangToolsMapperHelper {
 
  57     private static final Logger LOG = LoggerFactory.getLogger(YangToolsMapperHelper.class);
 
  58     private static final String TYPEOBJECT_INSTANCE_METHOD = "getDefaultInstance";
 
  59     private static final String BUILDER = "Builder";
 
  60     private static final DateTimeFormatter formatterOutput =
 
  61             DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.S'Z'").withZone(ZoneOffset.UTC);
 
  63     private static BundleContext context = getBundleContext();
 
  64     private static ConcurrentHashMap<String, Class<?>> cache = new ConcurrentHashMap<>();
 
  66     private YangToolsMapperHelper() {
 
  70     public static Class<?> findClass(String name) throws ClassNotFoundException {
 
  73         Class<?> res = cache.get(name);
 
  77         //Try first in actual bundle
 
  79             return loadClass(null, name);
 
  80         } catch (ClassNotFoundException e) {
 
  81             // No problem, this bundle doesn't have the class
 
  83         // Try to find in other bundles
 
  84         if (context != null) {
 
  86             for (Bundle b : context.getBundles()) {
 
  88                     return loadClass(b, name);
 
  89                 } catch (ClassNotFoundException e) {
 
  90                     // No problem, this bundle doesn't have the class
 
  94         // really not found in any bundle
 
  95         throw new ClassNotFoundException("Can not find class '" + name + "'");
 
  98     private static Class<?> loadClass(Bundle b, String name) throws ClassNotFoundException {
 
  99         Class<?> res = b == null ? Class.forName(name) : b.loadClass(name);
 
 100         cache.put(name, res);
 
 105      * Verify if builder is available
 
 107      * @throws ClassNotFoundException
 
 109     public static Class<?> assertBuilderClass(Class<?> clazz) throws ClassNotFoundException {
 
 110         return getBuilderClass(getBuilderClassName(clazz));
 
 113     public static Class<?> getBuilderClass(String name) throws ClassNotFoundException {
 
 114         return findClass(name);
 
 117     public static Class<?> getBuilderClass(Class<?> clazz) throws ClassNotFoundException {
 
 118         return findClass(getBuilderClassName(clazz));
 
 122      * Create name of builder class
 
 126      * @return builders class name
 
 127      * @throws ClassNotFoundException
 
 129     public static String getBuilderClassName(Class<?> clazz) {
 
 130         return clazz.getName() + BUILDER;
 
 133     @SuppressWarnings("unchecked")
 
 134     public static <B extends Builder<?>> Class<B> findBuilderClass(DeserializationContext ctxt, Class<?> clazz)
 
 135             throws ClassNotFoundException {
 
 136         return (Class<B>) findClass(getBuilderClassName(clazz));
 
 139     public static <B extends Builder<?>> Optional<Class<B>> findBuilderClassOptional(DeserializationContext ctxt,
 
 142             return Optional.of(findBuilderClass(ctxt, clazz));
 
 143         } catch (ClassNotFoundException e) {
 
 144             return Optional.empty();
 
 148     public static boolean hasClassDeclaredMethod(Class<?> clazz, String name) {
 
 149         Method[] methods = clazz.getDeclaredMethods();
 
 150         for (Method m : methods) {
 
 151             if (m.getName().equals(name)) {
 
 158     @SuppressWarnings("unchecked")
 
 159     public static <T> Optional<T> getInstanceByConstructor(Class<?> clazz, String arg)
 
 160             throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
 
 161             NoSuchMethodException, SecurityException {
 
 162         List<Class<?>> ctypes = getConstructorParameterTypes(clazz, String.class);
 
 163         Optional<Object> oObj;
 
 164         for (Class<?> ctype : ctypes) {
 
 165             if (ctype.equals(String.class)) {
 
 166                 return Optional.of((T) clazz.getConstructor(ctype).newInstance(arg));
 
 167             } else if ((oObj = getDefaultInstance(ctype, arg)).isPresent()) {
 
 168                 return Optional.of((T) clazz.getConstructor(ctype).newInstance(oObj.get()));
 
 170                 // TODO: recursive instantiation down to string constructor or
 
 171                 // getDefaultInstance method
 
 172                 LOG.debug("Not implemented arg:'{}' class:'{}'", arg, clazz);
 
 175         return Optional.empty();
 
 178     @SuppressWarnings("unchecked")
 
 179     public static <T> Optional<T> getDefaultInstance(@Nullable Class<?> clazz, String arg) throws NoSuchMethodException,
 
 180             SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
 181         LOG.trace("arg:'{}' clazz '{}'", arg, clazz != null ? clazz.getName() : "null");
 
 183             Method[] methods = clazz.getDeclaredMethods();
 
 184             for (Method m : methods) {
 
 185                 //TODO Verify argument type to avoid exception
 
 186                 if (m.getName().equals(TYPEOBJECT_INSTANCE_METHOD)) {
 
 187                     Method method = clazz.getDeclaredMethod(TYPEOBJECT_INSTANCE_METHOD, String.class);
 
 188                     LOG.trace("Invoke {} available {}", TYPEOBJECT_INSTANCE_METHOD, method != null);
 
 189                     return Optional.of((T) method.invoke(null, arg));
 
 193         return Optional.empty();
 
 196     public static <T> Optional<T> getDefaultInstance(Optional<Class<T>> optionalClazz, String arg)
 
 197             throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
 
 198             InvocationTargetException {
 
 199         if (optionalClazz.isPresent()) {
 
 200             return getDefaultInstance(optionalClazz.get(), arg);
 
 202         return Optional.empty();
 
 205     public static List<Class<?>> getConstructorParameterTypes(Class<?> clazz, Class<?> prefer) {
 
 207         Constructor<?>[] constructors = clazz.getConstructors();
 
 208         List<Class<?>> res = new ArrayList<>();
 
 209         for (Constructor<?> c : constructors) {
 
 210             Class<?>[] ptypes = c.getParameterTypes();
 
 211             if (ptypes.length == 1) {
 
 215             if (prefer != null && ptypes.length == 1 && ptypes[0].equals(prefer)) {
 
 216                 return Arrays.asList(prefer);
 
 222     public static boolean implementsInterface(Class<?> clz, Class<?> ifToImplement) {
 
 223         if (clz.equals(ifToImplement)) {
 
 226         Class<?>[] ifs = clz.getInterfaces();
 
 227         for (Class<?> iff : ifs) {
 
 228             if (iff.equals(ifToImplement)) {
 
 232         return ifToImplement.isAssignableFrom(clz);
 
 236      * Provide mapping of string to attribute names, generated by yang-tools. "netconf-id" converted to "_netconfId"
 
 238      * @param name with attribute name, not null or empty
 
 239      * @return converted string or null if name was empty or null
 
 241     public @Nullable static String toCamelCaseAttributeName(final String name) {
 
 242         if (name == null || name.isEmpty())
 
 245         final StringBuilder ret = new StringBuilder(name.length());
 
 246         if (!name.startsWith("_"))
 
 248         ret.append(toCamelCase(name));
 
 249         return ret.toString();
 
 252     public static String toCamelCase(final String name) {
 
 254         final StringBuilder ret = new StringBuilder(name.length());
 
 255         for (final String word : name.split("-")) {
 
 256             if (!word.isEmpty()) {
 
 258                     ret.append(Character.toLowerCase(word.charAt(0)));
 
 260                     ret.append(Character.toUpperCase(word.charAt(0)));
 
 262                 ret.append(word.substring(1));
 
 265         return ret.toString();
 
 268     public static String toCamelCaseClassName(final String name) {
 
 269         final String clsName = toCamelCase(name);
 
 270         return clsName.substring(0, 1).toUpperCase() + clsName.substring(1);
 
 273     private static BundleContext getBundleContext() {
 
 274         Bundle bundle = FrameworkUtil.getBundle(YangToolsMapperHelper.class);
 
 275         return bundle != null ? bundle.getBundleContext() : null;
 
 278     public static boolean hasTime(Notification notification) {
 
 279         return notification instanceof EventInstantAware;
 
 282     public static boolean hasTime(DOMNotification notification) {
 
 283         return notification instanceof DOMEvent;
 
 286     public static DateAndTime getTime(Notification notification, Instant defaultValue) {
 
 288         if (hasTime(notification)) { // If notification class extends/implements the EventInstantAware
 
 289             time = ((EventInstantAware) notification).eventInstant();
 
 290             LOG.debug("Event time {}", time);
 
 293             LOG.debug("Defaulting to actual time of processing the notification - {}", time);
 
 295         return DateAndTime.getDefaultInstance(ZonedDateTime.ofInstant(time, ZoneOffset.UTC).format(formatterOutput));
 
 298     public static DateAndTime getTime(DOMNotification notification, Instant defaultValue) {
 
 300         if (hasTime(notification)) { // If notification class extends/implements the EventInstantAware
 
 301             time = ((DOMEvent) notification).getEventInstant();
 
 302             LOG.debug("Event time {}", time);
 
 305             LOG.debug("Defaulting to actual time of processing the notification - {}", time);
 
 307         return DateAndTime.getDefaultInstance(ZonedDateTime.ofInstant(time, ZoneOffset.UTC).format(formatterOutput));
 
 311     public static <K extends Identifier<V>, V extends Identifiable<K>> Map<K,V> toMap(List<V> list) {
 
 312          return list == null || list.isEmpty() ? null : Maps.uniqueIndex(list, Identifiable::key);
 
 315     @SuppressWarnings("unchecked")
 
 316         public static <S,T> T callBuild(S builder) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 
 317         Method method = builder.getClass().getMethod("build");
 
 318                 return (T) method.invoke(builder);