*/
package org.opendaylight.mdsal.binding.runtime.spi;
+import static com.google.common.base.Preconditions.checkState;
+
import com.google.common.annotations.Beta;
-import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableSet;
import java.util.Arrays;
import java.util.Collection;
+import java.util.ServiceLoader;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeGenerator;
import org.opendaylight.mdsal.binding.runtime.api.DefaultBindingRuntimeContext;
-import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
+import org.opendaylight.mdsal.binding.runtime.api.ModuleInfoSnapshot;
+import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.contract.Naming;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-import org.opendaylight.yangtools.yang.model.parser.api.YangParserFactory;
+import org.opendaylight.yangtools.yang.parser.api.YangParserException;
+import org.opendaylight.yangtools.yang.parser.api.YangParserFactory;
/**
* Simple helpers to help with reconstruction of BindingRuntimeContext from generated binding classes. These involve
@Beta
public final class BindingRuntimeHelpers {
private BindingRuntimeHelpers() {
-
+ // Hidden on purpose
}
public static @NonNull EffectiveModelContext createEffectiveModel(final Class<?>... classes) {
return createEffectiveModel(Arrays.stream(classes)
- .map(BindingRuntimeHelpers::extractYangModuleInfo)
+ .map(BindingRuntimeHelpers::getYangModuleInfo)
.collect(Collectors.toList()));
}
public static @NonNull EffectiveModelContext createEffectiveModel(
final Iterable<? extends YangModuleInfo> moduleInfos) {
- return createEffectiveModel(ServiceLoaderState.ParserFactory.INSTANCE, moduleInfos);
+ try {
+ return createEffectiveModel(ServiceLoaderState.ParserFactory.INSTANCE, moduleInfos);
+ } catch (YangParserException e) {
+ throw new IllegalStateException("Failed to parse models", e);
+ }
}
public static @NonNull EffectiveModelContext createEffectiveModel(final YangParserFactory parserFactory,
- final Iterable<? extends YangModuleInfo> moduleInfos) {
- return prepareContext(parserFactory, moduleInfos).tryToCreateModelContext().orElseThrow();
+ final Iterable<? extends YangModuleInfo> moduleInfos) throws YangParserException {
+ return prepareContext(parserFactory, moduleInfos).modelContext();
}
public static @NonNull BindingRuntimeContext createRuntimeContext() {
- final ModuleInfoBackedContext ctx = prepareContext(ServiceLoaderState.ParserFactory.INSTANCE,
- BindingReflections.loadModuleInfos());
- return DefaultBindingRuntimeContext.create(ServiceLoaderState.Generator.INSTANCE.generateTypeMapping(
- ctx.tryToCreateModelContext().orElseThrow()), ctx);
+ final ModuleInfoSnapshot infos;
+ try {
+ infos = prepareContext(ServiceLoaderState.ParserFactory.INSTANCE, loadModuleInfos());
+ } catch (YangParserException e) {
+ throw new IllegalStateException("Failed to parse models", e);
+ }
+ return new DefaultBindingRuntimeContext(ServiceLoaderState.Generator.INSTANCE.generateTypeMapping(
+ infos.modelContext()), infos);
}
public static @NonNull BindingRuntimeContext createRuntimeContext(final Class<?>... classes) {
- return createRuntimeContext(ServiceLoaderState.ParserFactory.INSTANCE, ServiceLoaderState.Generator.INSTANCE,
- classes);
+ try {
+ return createRuntimeContext(ServiceLoaderState.ParserFactory.INSTANCE,
+ ServiceLoaderState.Generator.INSTANCE, classes);
+ } catch (YangParserException e) {
+ throw new IllegalStateException("Failed to parse models", e);
+ }
+ }
+
+ public static @NonNull BindingRuntimeContext createRuntimeContext(
+ final Collection<? extends YangModuleInfo> infos) {
+ final ModuleInfoSnapshot snapshot;
+
+ try {
+ snapshot = prepareContext(ServiceLoaderState.ParserFactory.INSTANCE, infos);
+ } catch (YangParserException e) {
+ throw new IllegalStateException("Failed to parse models", e);
+ }
+
+ return new DefaultBindingRuntimeContext(
+ ServiceLoaderState.Generator.INSTANCE.generateTypeMapping(snapshot.modelContext()), snapshot);
}
public static @NonNull BindingRuntimeContext createRuntimeContext(final YangParserFactory parserFactory,
- final BindingRuntimeGenerator generator, final Class<?>... classes) {
+ final BindingRuntimeGenerator generator, final Class<?>... classes) throws YangParserException {
return createRuntimeContext(parserFactory, generator, Arrays.asList(classes));
}
public static @NonNull BindingRuntimeContext createRuntimeContext(final YangParserFactory parserFactory,
- final BindingRuntimeGenerator generator, final Collection<Class<?>> classes) {
- final ModuleInfoBackedContext ctx = prepareContext(parserFactory, classes.stream()
- .map(BindingRuntimeHelpers::extractYangModuleInfo)
+ final BindingRuntimeGenerator generator, final Collection<Class<?>> classes) throws YangParserException {
+ final var infos = prepareContext(parserFactory, classes.stream()
+ .map(BindingRuntimeHelpers::getYangModuleInfo)
.collect(Collectors.toList()));
- return DefaultBindingRuntimeContext.create(
- generator.generateTypeMapping(ctx.tryToCreateModelContext().orElseThrow()), ctx);
+ return new DefaultBindingRuntimeContext(generator.generateTypeMapping(infos.modelContext()), infos);
}
- @SuppressWarnings("checkstyle:IllegalCatch")
- static @NonNull YangModuleInfo extractYangModuleInfo(final Class<?> clazz) {
- try {
- return BindingReflections.getModuleInfo(clazz);
- } catch (Exception e) {
- Throwables.throwIfUnchecked(e);
- throw new IllegalStateException("Failed to extract module info from " + clazz, e);
+ public static @NonNull YangModuleInfo getYangModuleInfo(final Class<?> clazz) {
+ // Module info resides in the root package we will use that to ascertain identity
+ final var modelPackage = Naming.getModelRootPackageName(clazz.getPackage().getName());
+
+ for (var bindingProvider : ServiceLoader.load(YangModelBindingProvider.class, clazz.getClassLoader())) {
+ var moduleInfo = bindingProvider.getModuleInfo();
+ if (modelPackage.equals(moduleInfo.getClass().getPackage().getName())) {
+ return moduleInfo;
+ }
}
+ throw new IllegalStateException("Failed to find YangModuleInfo in package " + modelPackage + " for " + clazz);
}
- private static @NonNull ModuleInfoBackedContext prepareContext(final YangParserFactory parserFactory,
- final Iterable<? extends YangModuleInfo> moduleInfos) {
- final ModuleInfoBackedContext ctx = ModuleInfoBackedContext.create("helper", parserFactory);
- ctx.registerModuleInfos(moduleInfos);
- return ctx;
+ public static @NonNull ImmutableSet<YangModuleInfo> loadModuleInfos() {
+ return loadModuleInfos(Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * Loads {@link YangModuleInfo} infos available on supplied classloader.
+ *
+ * <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 classLoader Classloader for which {@link YangModuleInfo} should be retrieved.
+ * @return Set of {@link YangModuleInfo} available for supplied classloader.
+ */
+ public static @NonNull ImmutableSet<YangModuleInfo> loadModuleInfos(final ClassLoader classLoader) {
+ final var moduleInfoSet = ImmutableSet.<YangModuleInfo>builder();
+ for (var bindingProvider : ServiceLoader.load(YangModelBindingProvider.class, classLoader)) {
+ var moduleInfo = bindingProvider.getModuleInfo();
+ checkState(moduleInfo != null, "Module Info for %s is not available.", bindingProvider.getClass());
+ collectYangModuleInfo(bindingProvider.getModuleInfo(), moduleInfoSet);
+ }
+ return moduleInfoSet.build();
+ }
+
+ private static void collectYangModuleInfo(final YangModuleInfo moduleInfo,
+ final ImmutableSet.Builder<YangModuleInfo> moduleInfoSet) {
+ moduleInfoSet.add(moduleInfo);
+ for (var dependency : moduleInfo.getImportedModules()) {
+ collectYangModuleInfo(dependency, moduleInfoSet);
+ }
+ }
+
+ private static @NonNull ModuleInfoSnapshot prepareContext(final YangParserFactory parserFactory,
+ final Iterable<? extends YangModuleInfo> moduleInfos) throws YangParserException {
+ return new ModuleInfoSnapshotBuilder(parserFactory).add(moduleInfos).build();
}
}