Bump to odlparent-9.0.0/yangtools-7.0.1-SNAPSHOT
[mdsal.git] / yanglib / mdsal-yanglib-rfc8525 / src / main / java / org / opendaylight / mdsal / yanglib / rfc8525 / MountPointContextFactoryImpl.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech s.r.o. and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.yanglib.rfc8525;
9
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;
13
14 import java.net.MalformedURLException;
15 import java.net.URL;
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
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;
58
59 final class MountPointContextFactoryImpl extends AbstractMountPointContextFactory {
60     private static final Logger LOG = LoggerFactory.getLogger(MountPointContextFactoryImpl.class);
61
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;
68
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) {
74         super(mountId);
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);
80     }
81
82     @Override
83     protected MountPointContextFactory createContextFactory(final MountPointDefinition mountPoint) {
84         return new MountPointContextFactoryImpl(mountPoint.getIdentifier(), resolver, yangLibContext, identityCodec,
85             codec, legacyCodec);
86     }
87
88     @Override
89     protected Optional<EffectiveModelContext> findSchemaForLibrary(final ContainerName containerName) {
90         switch (containerName) {
91             case RFC7895:
92             case RFC8525:
93                 return Optional.of(yangLibContext);
94             default:
95                 LOG.debug("Unhandled YANG library container {}", containerName);
96                 return Optional.empty();
97         }
98     }
99
100     @Override
101     protected EffectiveModelContext bindLibrary(final ContainerName containerName, final ContainerNode libData)
102             throws YangParserException {
103         switch (containerName) {
104             case RFC7895:
105                 return bindLibrary(verifyNotNull(legacyCodec.deserialize(libData)));
106             case RFC8525:
107                 return bindLibrary(verifyNotNull(codec.deserialize(libData)));
108             default:
109                 throw new IllegalStateException("Unhandled container type " + containerName);
110         }
111     }
112
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");
116
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);
123             }
124         }
125         checkArgument(moduleSet.isEmpty(), "Failed to resolve module sets %s", moduleSet);
126
127         return resolver.resolveSchemaContext(librarySources, requiredSources);
128     }
129
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<>();
135
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());
139
140             // TODO: take deviations/features into account
141
142             if (ConformanceType.Import == module.getConformanceType()) {
143                 librarySources.add(modRef);
144             } else {
145                 requiredSources.add(modRef);
146             }
147
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()));
152             }
153         }
154
155         return resolver.resolveSchemaContext(librarySources, requiredSources);
156     }
157
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();
161
162         // FIXME: This is ugly, but it is the most compatible thing we can do without knowing the exact requested
163         //        datastore
164         if (it.hasNext() && !qname.equals(identityCodec.fromBinding(ds.getName()))) {
165             do {
166                 final Datastore next = it.next();
167                 if (qname.equals(identityCodec.fromBinding(ds.getName()))) {
168                     return next.getSchema();
169                 }
170             } while (it.hasNext());
171         }
172
173         return ds.getSchema();
174     }
175
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()));
180         if (uri != null) {
181             try {
182                 return SourceReference.of(sourceId, new URL(uri.getValue()));
183             } catch (MalformedURLException e) {
184                 LOG.debug("Ignoring invalid schema location {}", uri, e);
185             }
186         }
187
188         return SourceReference.of(sourceId);
189     }
190
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);
195         }
196         return new HashSet<>(schema.getModuleSet());
197     }
198
199     private static void fillModules(final List<SourceReference> librarySources,
200             final List<SourceReference> requiredSources, final ModuleSet modSet) {
201         // TODO: take deviations/features into account
202
203         for (ImportOnlyModule mod : modSet.nonnullImportOnlyModule().values()) {
204             fillSource(librarySources, mod.getName(), ImportOnlyModuleRevisionBuilder.toYangCommon(mod.getRevision()),
205                 mod.getLocation());
206             mod.nonnullSubmodule().values().forEach(sub -> {
207                 fillSource(librarySources, sub.getName(), toYangCommon(sub.getRevision()), sub.getLocation());
208             });
209         }
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());
214             });
215         }
216     }
217
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) {
225                 try {
226                     locations.add(new URL(uri.getValue()));
227                 } catch (MalformedURLException e) {
228                     LOG.debug("Ignoring invalid schema location {}", uri, e);
229                 }
230             }
231             sourceRef = SourceReference.of(sourceId, locations);
232         } else {
233             sourceRef = SourceReference.of(sourceId);
234         }
235
236         sources.add(sourceRef);
237     }
238
239     private static Optional<Revision> toYangCommon(final @Nullable RevisionIdentifier revisionIdentifier) {
240         return revisionIdentifier == null ? Optional.empty() : Optional.of(Revision.of(revisionIdentifier.getValue()));
241     }
242
243 }