import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+import com.google.common.annotations.Beta;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
-import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.annotation.RegEx;
+import org.checkerframework.checker.regex.qual.Regex;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
import org.opendaylight.yangtools.util.ClassLoaderUtils;
import org.opendaylight.yangtools.yang.binding.Action;
import org.opendaylight.yangtools.yang.binding.Augmentable;
import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.AugmentationHolder;
import org.opendaylight.yangtools.yang.binding.BaseIdentity;
import org.opendaylight.yangtools.yang.binding.ChildOf;
import org.opendaylight.yangtools.yang.binding.DataContainer;
private static final long EXPIRATION_TIME = 60;
- @RegEx
+ @Regex
private static final String ROOT_PACKAGE_PATTERN_STRING =
"(org.opendaylight.yang.gen.v1.[a-z0-9_\\.]*\\.(?:rev[0-9][0-9][0-1][0-9][0-3][0-9]|norev))";
private static final Pattern ROOT_PACKAGE_PATTERN = Pattern.compile(ROOT_PACKAGE_PATTERN_STRING);
.expireAfterAccess(EXPIRATION_TIME, TimeUnit.SECONDS)
.build(new ClassToQNameLoader());
+ private static final LoadingCache<ClassLoader, ImmutableSet<YangModuleInfo>> MODULE_INFO_CACHE =
+ CacheBuilder.newBuilder().weakKeys().weakValues().build(
+ new CacheLoader<ClassLoader, ImmutableSet<YangModuleInfo>>() {
+ @Override
+ public ImmutableSet<YangModuleInfo> load(final ClassLoader key) {
+ return loadModuleInfos(key);
+ }
+ });
+
private BindingReflections() {
throw new UnsupportedOperationException("Utility class.");
}
*/
public static Class<? extends Augmentable<?>> findAugmentationTarget(
final Class<? extends Augmentation<?>> augmentation) {
- return ClassLoaderUtils.findFirstGenericArgument(augmentation, Augmentation.class);
+ final Optional<Class<Augmentable<?>>> opt = ClassLoaderUtils.findFirstGenericArgument(augmentation,
+ Augmentation.class);
+ return opt.orElse(null);
}
/**
* @return Parent class, e.g. class of which the childClass is ChildOf.
*/
public static Class<?> findHierarchicalParent(final Class<? extends ChildOf<?>> childClass) {
- return ClassLoaderUtils.findFirstGenericArgument(childClass, ChildOf.class);
+ return ClassLoaderUtils.findFirstGenericArgument(childClass, ChildOf.class).orElse(null);
}
/**
* Find data hierarchy parent from concrete Data class. This method is shorthand which gets DataObject class by
- * invoking {@link DataObject#getImplementedInterface()} and uses {@link #findHierarchicalParent(Class)}.
+ * invoking {@link DataObject#implementedInterface()} and uses {@link #findHierarchicalParent(Class)}.
*
* @param child
* Child object for which the parent needs to be located.
*/
public static Class<?> findHierarchicalParent(final DataObject child) {
if (child instanceof ChildOf) {
- return ClassLoaderUtils.findFirstGenericArgument(child.getImplementedInterface(), ChildOf.class);
+ return ClassLoaderUtils.findFirstGenericArgument(child.implementedInterface(), ChildOf.class).orElse(null);
}
return null;
}
// resolveRpcInputClass() check.While RpcMethodInvoker counts with one argument for
// non input type and two arguments for input type, resolveRpcInputClass() counting
// with zero for non input and one for input type
- && possibleMethod.getParameterTypes().length <= 2;
+ && possibleMethod.getParameterCount() <= 2;
}
/**
public static Optional<Class<?>> resolveRpcOutputClass(final Method targetMethod) {
checkState(isRpcMethod(targetMethod), "Supplied method is not a RPC invocation method");
Type futureType = targetMethod.getGenericReturnType();
- Type rpcResultType = ClassLoaderUtils.getFirstGenericParameter(futureType);
- Type rpcResultArgument = ClassLoaderUtils.getFirstGenericParameter(rpcResultType);
+ Type rpcResultType = ClassLoaderUtils.getFirstGenericParameter(futureType).orElse(null);
+ Type rpcResultArgument = ClassLoaderUtils.getFirstGenericParameter(rpcResultType).orElse(null);
if (rpcResultArgument instanceof Class && !Void.class.equals(rpcResultArgument)) {
return Optional.of((Class) rpcResultArgument);
}
return Optional.empty();
}
- public static QName getQName(final Class<? extends BaseIdentity> context) {
- return findQName(context);
+ public static @NonNull QName getQName(final Class<? extends BaseIdentity> bindingClass) {
+ final Optional<QName> qname = CLASS_TO_QNAME.getUnchecked(requireNonNull(bindingClass));
+ checkState(qname.isPresent(), "Failed to resolve QName of %s", bindingClass);
+ return qname.get();
}
/**
}
/**
- * Returns root package name for suplied package.
+ * Returns root package name for supplied package.
*
* @param pkg
* Package for which find model root package.
*/
public static boolean isNotificationCallback(final Method method) {
checkArgument(method != null);
- if (method.getName().startsWith("on") && method.getParameterTypes().length == 1) {
+ if (method.getName().startsWith("on") && method.getParameterCount() == 1) {
Class<?> potentialNotification = method.getParameterTypes()[0];
if (isNotification(potentialNotification)
&& method.getName().equals("on" + potentialNotification.getSimpleName())) {
* When {@link YangModuleInfo} is available, all dependencies are recursively collected into returning set by
* collecting results of {@link YangModuleInfo#getImportedModules()}.
*
- * @param loader
- * Classloader for which {@link YangModuleInfo} should be
- * retrieved.
+ * <p>
+ * Consider using {@link #cacheModuleInfos(ClassLoader)} if the classloader is known to be immutable.
+ *
+ * @param loader Classloader for which {@link YangModuleInfo} should be retrieved.
* @return Set of {@link YangModuleInfo} available for supplied classloader.
*/
public static ImmutableSet<YangModuleInfo> loadModuleInfos(final ClassLoader loader) {
return moduleInfoSet.build();
}
+ /**
+ * Loads {@link YangModuleInfo} instances available on supplied {@link ClassLoader}, assuming the set of available
+ * information does not change. Subsequent accesses may return cached values.
+ *
+ * <p>
+ * {@link YangModuleInfo} are discovered using {@link ServiceLoader} for {@link YangModelBindingProvider}.
+ * {@link YangModelBindingProvider} are simple classes which holds only pointers to actual instance
+ * {@link YangModuleInfo}.
+ *
+ * <p>
+ * When {@link YangModuleInfo} is available, all dependencies are recursively collected into returning set by
+ * collecting results of {@link YangModuleInfo#getImportedModules()}.
+ *
+ * @param loader Class loader for which {@link YangModuleInfo} should be retrieved.
+ * @return Set of {@link YangModuleInfo} available for supplied classloader.
+ */
+ @Beta
+ public static ImmutableSet<YangModuleInfo> cacheModuleInfos(final ClassLoader loader) {
+ return MODULE_INFO_CACHE.getUnchecked(loader);
+ }
+
private static void collectYangModuleInfo(final YangModuleInfo moduleInfo,
final Builder<YangModuleInfo> moduleInfoSet) {
moduleInfoSet.add(moduleInfo);
checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type must be derived from DataContainer");
List<Class<? extends DataObject>> ret = new LinkedList<>();
for (Method method : type.getMethods()) {
- Optional<Class<? extends DataContainer>> entity = getYangModeledReturnType(method);
+ Optional<Class<? extends DataContainer>> entity = getYangModeledReturnType(method,
+ BindingMapping.GETTER_PREFIX);
if (entity.isPresent()) {
ret.add((Class<? extends DataObject>) entity.get());
}
* @return Iterable of all data children, which have YANG modeled entity
*/
public static Map<Class<?>, Method> getChildrenClassToMethod(final Class<?> type) {
+ return getChildrenClassToMethod(type, BindingMapping.GETTER_PREFIX);
+ }
+
+ private static Map<Class<?>, Method> getChildrenClassToMethod(final Class<?> type, final String prefix) {
checkArgument(type != null, "Target type must not be null");
checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type %s must be derived from DataContainer",
type);
Map<Class<?>, Method> ret = new HashMap<>();
for (Method method : type.getMethods()) {
- Optional<Class<? extends DataContainer>> entity = getYangModeledReturnType(method);
+ Optional<Class<? extends DataContainer>> entity = getYangModeledReturnType(method, prefix);
if (entity.isPresent()) {
ret.put(entity.get(), method);
}
return ret;
}
- @SuppressWarnings({ "unchecked", "rawtypes", "checkstyle:illegalCatch" })
- private static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method) {
- if ("getClass".equals(method.getName()) || !method.getName().startsWith("get")
- || method.getParameterTypes().length > 0) {
+ @Beta
+ public static Map<Class<?>, Method> getChildrenClassToNonnullMethod(final Class<?> type) {
+ return getChildrenClassToMethod(type, BindingMapping.NONNULL_PREFIX);
+ }
+
+ @SuppressWarnings("checkstyle:illegalCatch")
+ private static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method,
+ final String prefix) {
+ final String methodName = method.getName();
+ if ("getClass".equals(methodName) || !methodName.startsWith(prefix) || method.getParameterCount() > 0) {
return Optional.empty();
}
- Class returnType = method.getReturnType();
+ final Class<?> returnType = method.getReturnType();
if (DataContainer.class.isAssignableFrom(returnType)) {
- return Optional.of(returnType);
+ return optionalDataContainer(returnType);
} else if (List.class.isAssignableFrom(returnType)) {
try {
return ClassLoaderUtils.callWithClassLoader(method.getDeclaringClass().getClassLoader(), () -> {
- Type listResult = ClassLoaderUtils.getFirstGenericParameter(method.getGenericReturnType());
- if (listResult instanceof Class
- && DataContainer.class.isAssignableFrom((Class) listResult)) {
- return Optional.of((Class) listResult);
- }
- return Optional.empty();
+ return ClassLoaderUtils.getFirstGenericParameter(method.getGenericReturnType()).flatMap(
+ result -> result instanceof Class ? optionalCast((Class<?>) result) : Optional.empty());
});
} catch (Exception e) {
/*
return Optional.empty();
}
+ private static Optional<Class<? extends DataContainer>> optionalCast(final Class<?> type) {
+ return DataContainer.class.isAssignableFrom(type) ? optionalDataContainer(type) : Optional.empty();
+ }
+
+ private static Optional<Class<? extends DataContainer>> optionalDataContainer(final Class<?> type) {
+ return Optional.of(type.asSubclass(DataContainer.class));
+ }
+
private static class ClassToQNameLoader extends CacheLoader<Class<?>, Optional<QName>> {
@Override
* @return Map of augmentations if read was successful, otherwise empty map.
*/
public static Map<Class<? extends Augmentation<?>>, Augmentation<?>> getAugmentations(final Augmentable<?> input) {
+ if (input instanceof AugmentationHolder) {
+ return ((AugmentationHolder) input).augmentations();
+ }
return AugmentationFieldGetter.getGetter(input.getClass()).getAugmentations(input);
}
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static boolean isSubstitutionFor(final Class potential, final Class target) {
- HashSet<Class> subImplemented = Sets.newHashSet(potential.getInterfaces());
- HashSet<Class> targetImplemented = Sets.newHashSet(target.getInterfaces());
+ Set<Class> subImplemented = new HashSet<>(Arrays.asList(potential.getInterfaces()));
+ Set<Class> targetImplemented = new HashSet<>(Arrays.asList(target.getInterfaces()));
if (!subImplemented.equals(targetImplemented)) {
return false;
}