2 * Copyright (c) 2019 PANTHEON.tech s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.yanglib.rfc8525;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verifyNotNull;
12 import static java.util.Objects.requireNonNull;
14 import java.net.MalformedURLException;
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
21 import java.util.Optional;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
25 import org.opendaylight.mdsal.binding.dom.codec.api.BindingIdentityCodec;
26 import org.opendaylight.mdsal.yanglib.api.SchemaContextResolver;
27 import org.opendaylight.mdsal.yanglib.api.SourceReference;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.datastores.rev180214.Operational;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesState;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.RevisionIdentifier;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibrary;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.CommonLeafs;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.CommonLeafsRevisionBuilder;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.Module.ConformanceType;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.ImportOnlyModule;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.ImportOnlyModuleRevisionBuilder;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.Module;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.Datastore;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.DatastoreKey;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.ModuleSet;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.Schema;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.SchemaKey;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.YangIdentifier;
45 import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
46 import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
47 import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants.ContainerName;
48 import org.opendaylight.yangtools.rfc8528.data.util.AbstractMountPointContextFactory;
49 import org.opendaylight.yangtools.yang.common.QName;
50 import org.opendaylight.yangtools.yang.common.Revision;
51 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
52 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
53 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
54 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
55 import org.opendaylight.yangtools.yang.parser.api.YangParserException;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
59 final class MountPointContextFactoryImpl extends AbstractMountPointContextFactory {
60 private static final Logger LOG = LoggerFactory.getLogger(MountPointContextFactoryImpl.class);
62 @SuppressWarnings("deprecation")
63 private final BindingDataObjectCodecTreeNode<ModulesState> legacyCodec;
64 private final BindingDataObjectCodecTreeNode<YangLibrary> codec;
65 private final BindingIdentityCodec identityCodec;
66 private final EffectiveModelContext yangLibContext;
67 private final SchemaContextResolver resolver;
69 MountPointContextFactoryImpl(final MountPointIdentifier mountId, final SchemaContextResolver resolver,
70 final EffectiveModelContext yangLibContext,
71 final BindingIdentityCodec identityCodec,
72 final BindingDataObjectCodecTreeNode<YangLibrary> codec,
73 final BindingDataObjectCodecTreeNode<ModulesState> legacyCodec) {
75 this.resolver = requireNonNull(resolver);
76 this.identityCodec = requireNonNull(identityCodec);
77 this.yangLibContext = requireNonNull(yangLibContext);
78 this.codec = requireNonNull(codec);
79 this.legacyCodec = requireNonNull(legacyCodec);
83 protected MountPointContextFactory createContextFactory(final MountPointDefinition mountPoint) {
84 return new MountPointContextFactoryImpl(mountPoint.getIdentifier(), resolver, yangLibContext, identityCodec,
89 protected Optional<EffectiveModelContext> findSchemaForLibrary(final ContainerName containerName) {
90 switch (containerName) {
93 return Optional.of(yangLibContext);
95 LOG.debug("Unhandled YANG library container {}", containerName);
96 return Optional.empty();
101 protected EffectiveModelContext bindLibrary(final ContainerName containerName, final ContainerNode libData)
102 throws YangParserException {
103 switch (containerName) {
105 return bindLibrary(verifyNotNull(legacyCodec.deserialize(libData)));
107 return bindLibrary(verifyNotNull(codec.deserialize(libData)));
109 throw new IllegalStateException("Unhandled container type " + containerName);
113 private @NonNull EffectiveModelContext bindLibrary(final @NonNull YangLibrary yangLib) throws YangParserException {
114 final Map<DatastoreKey, Datastore> datastores = yangLib.nonnullDatastore();
115 checkArgument(!datastores.isEmpty(), "No datastore defined");
117 final List<SourceReference> requiredSources = new ArrayList<>();
118 final List<SourceReference> librarySources = new ArrayList<>();
119 final HashSet<String> moduleSet = findModuleSet(yangLib, findSchemaName(datastores, Operational.QNAME));
120 for (ModuleSet modSet : yangLib.nonnullModuleSet().values()) {
121 if (moduleSet.remove(modSet.getName())) {
122 fillModules(librarySources, requiredSources, modSet);
125 checkArgument(moduleSet.isEmpty(), "Failed to resolve module sets %s", moduleSet);
127 return resolver.resolveSchemaContext(librarySources, requiredSources);
130 @SuppressWarnings("deprecation")
131 private @NonNull EffectiveModelContext bindLibrary(final @NonNull ModulesState modState)
132 throws YangParserException {
133 final List<SourceReference> requiredSources = new ArrayList<>();
134 final List<SourceReference> librarySources = new ArrayList<>();
136 for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list
137 .Module module : modState.nonnullModule().values()) {
138 final SourceReference modRef = sourceRefFor(module, module.getSchema());
140 // TODO: take deviations/features into account
142 if (ConformanceType.Import == module.getConformanceType()) {
143 librarySources.add(modRef);
145 requiredSources.add(modRef);
148 for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list
149 .module.Submodule submodule : module.nonnullSubmodule().values()) {
150 // Submodules go to library, as they are pulled in as needed
151 librarySources.add(sourceRefFor(submodule, submodule.getSchema()));
155 return resolver.resolveSchemaContext(librarySources, requiredSources);
158 private String findSchemaName(final Map<DatastoreKey, Datastore> datastores, final QName qname) {
159 final Iterator<Datastore> it = datastores.values().iterator();
160 final Datastore ds = it.next();
162 // FIXME: This is ugly, but it is the most compatible thing we can do without knowing the exact requested
164 if (it.hasNext() && !qname.equals(identityCodec.fromBinding(ds.getName()))) {
166 final Datastore next = it.next();
167 if (qname.equals(identityCodec.fromBinding(ds.getName()))) {
168 return next.getSchema();
170 } while (it.hasNext());
173 return ds.getSchema();
176 @SuppressWarnings("deprecation")
177 private static SourceReference sourceRefFor(final CommonLeafs obj, final Uri uri) {
178 final SourceIdentifier sourceId = RevisionSourceIdentifier.create(obj.getName().getValue(),
179 CommonLeafsRevisionBuilder.toYangCommon(obj.getRevision()));
182 return SourceReference.of(sourceId, new URL(uri.getValue()));
183 } catch (MalformedURLException e) {
184 LOG.debug("Ignoring invalid schema location {}", uri, e);
188 return SourceReference.of(sourceId);
191 private static HashSet<String> findModuleSet(final YangLibrary yangLib, final String schemaName) {
192 final Schema schema = yangLib.nonnullSchema().get(new SchemaKey(schemaName));
193 if (schema == null) {
194 throw new IllegalArgumentException("Failed to find moduleSet for " + schemaName);
196 return new HashSet<>(schema.getModuleSet());
199 private static void fillModules(final List<SourceReference> librarySources,
200 final List<SourceReference> requiredSources, final ModuleSet modSet) {
201 // TODO: take deviations/features into account
203 for (ImportOnlyModule mod : modSet.nonnullImportOnlyModule().values()) {
204 fillSource(librarySources, mod.getName(), ImportOnlyModuleRevisionBuilder.toYangCommon(mod.getRevision()),
206 mod.nonnullSubmodule().values().forEach(sub -> {
207 fillSource(librarySources, sub.getName(), toYangCommon(sub.getRevision()), sub.getLocation());
210 for (Module mod : modSet.nonnullModule().values()) {
211 fillSource(requiredSources, mod.getName(), toYangCommon(mod.getRevision()), mod.getLocation());
212 mod.nonnullSubmodule().values().forEach(sub -> {
213 fillSource(librarySources, sub.getName(), toYangCommon(sub.getRevision()), sub.getLocation());
218 private static void fillSource(final List<SourceReference> sources, final YangIdentifier sourceName,
219 final Optional<Revision> revision, final List<Uri> uris) {
220 final SourceIdentifier sourceId = RevisionSourceIdentifier.create(sourceName.getValue(), revision);
221 final SourceReference sourceRef;
222 if (uris != null && uris.isEmpty()) {
223 final List<URL> locations = new ArrayList<>();
224 for (Uri uri : uris) {
226 locations.add(new URL(uri.getValue()));
227 } catch (MalformedURLException e) {
228 LOG.debug("Ignoring invalid schema location {}", uri, e);
231 sourceRef = SourceReference.of(sourceId, locations);
233 sourceRef = SourceReference.of(sourceId);
236 sources.add(sourceRef);
239 private static Optional<Revision> toYangCommon(final @Nullable RevisionIdentifier revisionIdentifier) {
240 return revisionIdentifier == null ? Optional.empty() : Optional.of(Revision.of(revisionIdentifier.getValue()));