Expose IdentityCodec from BindingCodecTree
[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.Optional;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
24 import org.opendaylight.mdsal.binding.dom.codec.api.BindingIdentityCodec;
25 import org.opendaylight.mdsal.yanglib.api.SchemaContextResolver;
26 import org.opendaylight.mdsal.yanglib.api.SourceReference;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.datastores.rev180214.Operational;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesState;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.RevisionIdentifier;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibrary;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.CommonLeafs;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.CommonLeafsRevisionBuilder;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.Module.ConformanceType;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.ImportOnlyModule;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.ImportOnlyModuleRevisionBuilder;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.Module;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.Datastore;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.ModuleSet;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.Schema;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.YangIdentifier;
42 import org.opendaylight.yangtools.rcf8528.data.util.AbstractMountPointContextFactory;
43 import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
44 import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
45 import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants.ContainerName;
46 import org.opendaylight.yangtools.yang.common.QName;
47 import org.opendaylight.yangtools.yang.common.Revision;
48 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
49 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
50 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
51 import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
52 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
53 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 final class MountPointContextFactoryImpl extends AbstractMountPointContextFactory {
58     private static final Logger LOG = LoggerFactory.getLogger(MountPointContextFactoryImpl.class);
59
60     @SuppressWarnings("deprecation")
61     private final BindingDataObjectCodecTreeNode<ModulesState> legacyCodec;
62     private final BindingDataObjectCodecTreeNode<YangLibrary> codec;
63     private final BindingIdentityCodec identityCodec;
64     private final EffectiveModelContext yangLibContext;
65     private final SchemaContextResolver resolver;
66
67     MountPointContextFactoryImpl(final MountPointIdentifier mountId, final SchemaContextResolver resolver,
68             final EffectiveModelContext yangLibContext,
69             final BindingIdentityCodec identityCodec,
70             final BindingDataObjectCodecTreeNode<YangLibrary> codec,
71             final BindingDataObjectCodecTreeNode<ModulesState> legacyCodec) {
72         super(mountId);
73         this.resolver = requireNonNull(resolver);
74         this.identityCodec = requireNonNull(identityCodec);
75         this.yangLibContext = requireNonNull(yangLibContext);
76         this.codec = requireNonNull(codec);
77         this.legacyCodec = requireNonNull(legacyCodec);
78     }
79
80     @Override
81     protected MountPointContextFactory createContextFactory(final MountPointDefinition mountPoint) {
82         return new MountPointContextFactoryImpl(mountPoint.getIdentifier(), resolver, yangLibContext, identityCodec,
83             codec, legacyCodec);
84     }
85
86     @Override
87     protected Optional<SchemaContext> findSchemaForLibrary(final ContainerName containerName) {
88         switch (containerName) {
89             case RFC7895:
90             case RFC8525:
91                 return Optional.of(yangLibContext);
92             default:
93                 LOG.debug("Unhandled YANG library container {}", containerName);
94                 return Optional.empty();
95         }
96     }
97
98     @Override
99     protected SchemaContext bindLibrary(final ContainerName containerName, final ContainerNode libData)
100             throws YangParserException {
101         switch (containerName) {
102             case RFC7895:
103                 return bindLibrary(verifyNotNull(legacyCodec.deserialize(libData)));
104             case RFC8525:
105                 return bindLibrary(verifyNotNull(codec.deserialize(libData)));
106             default:
107                 throw new IllegalStateException("Unhandled container type " + containerName);
108         }
109     }
110
111     private @NonNull SchemaContext bindLibrary(final @NonNull YangLibrary yangLib) throws YangParserException {
112         final List<Datastore> datastores = yangLib.nonnullDatastore();
113         checkArgument(!datastores.isEmpty(), "No datastore defined");
114
115         final List<SourceReference> requiredSources = new ArrayList<>();
116         final List<SourceReference> librarySources = new ArrayList<>();
117         final HashSet<String> moduleSet = findModuleSet(yangLib, findSchemaName(datastores, Operational.QNAME));
118         for (ModuleSet modSet : yangLib.nonnullModuleSet()) {
119             if (moduleSet.remove(modSet.getName())) {
120                 fillModules(librarySources, requiredSources, modSet);
121             }
122         }
123         checkArgument(moduleSet.isEmpty(), "Failed to resolve module sets %s", moduleSet);
124
125         return resolver.resolveSchemaContext(librarySources, requiredSources);
126     }
127
128     @SuppressWarnings("deprecation")
129     private @NonNull SchemaContext bindLibrary(final @NonNull ModulesState modState) throws YangParserException {
130         final List<SourceReference> requiredSources = new ArrayList<>();
131         final List<SourceReference> librarySources = new ArrayList<>();
132
133         for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list
134                 .Module module : modState.nonnullModule()) {
135             final SourceReference modRef = sourceRefFor(module, module.getSchema());
136
137             // TODO: take deviations/features into account
138
139             if (ConformanceType.Import == module.getConformanceType()) {
140                 librarySources.add(modRef);
141             } else {
142                 requiredSources.add(modRef);
143             }
144
145             for (org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list
146                     .module.Submodule submodule : module.nonnullSubmodule()) {
147                 // Submodules go to library, as they are pulled in as needed
148                 librarySources.add(sourceRefFor(submodule, submodule.getSchema()));
149             }
150         }
151
152         return resolver.resolveSchemaContext(librarySources, requiredSources);
153     }
154
155     private String findSchemaName(final List<Datastore> datastores, final QName qname) {
156         final Iterator<Datastore> it = datastores.iterator();
157         final Datastore ds = it.next();
158
159         // FIXME: This is ugly, but it is the most compatible thing we can do without knowing the exact requested
160         //        datastore
161         if (it.hasNext() && !qname.equals(identityCodec.fromBinding(ds.getName()))) {
162             do {
163                 final Datastore next = it.next();
164                 if (qname.equals(identityCodec.fromBinding(ds.getName()))) {
165                     return next.getSchema();
166                 }
167             } while (it.hasNext());
168         }
169
170         return ds.getSchema();
171     }
172
173     @SuppressWarnings("deprecation")
174     private static SourceReference sourceRefFor(final CommonLeafs obj, final Uri uri) {
175         final SourceIdentifier sourceId = RevisionSourceIdentifier.create(obj.getName().getValue(),
176             CommonLeafsRevisionBuilder.toYangCommon(obj.getRevision()));
177         if (uri != null) {
178             try {
179                 return SourceReference.of(sourceId, new URL(uri.getValue()));
180             } catch (MalformedURLException e) {
181                 LOG.debug("Ignoring invalid schema location {}", uri, e);
182             }
183         }
184
185         return SourceReference.of(sourceId);
186     }
187
188     private static HashSet<String> findModuleSet(final YangLibrary yangLib, final String schemaName) {
189         for (Schema schema : yangLib.nonnullSchema()) {
190             if (schemaName.equals(schema.getName())) {
191                 return new HashSet<>(schema.getModuleSet());
192             }
193         }
194         throw new IllegalArgumentException("Failed to find moduleSet for " + schemaName);
195     }
196
197     private static void fillModules(final List<SourceReference> librarySources,
198             final List<SourceReference> requiredSources, final ModuleSet modSet) {
199         // TODO: take deviations/features into account
200
201         for (ImportOnlyModule mod : modSet.nonnullImportOnlyModule()) {
202             fillSource(librarySources, mod.getName(), ImportOnlyModuleRevisionBuilder.toYangCommon(mod.getRevision()),
203                 mod.getLocation());
204             mod.nonnullSubmodule().forEach(sub -> {
205                 fillSource(librarySources, sub.getName(), toYangCommon(sub.getRevision()), sub.getLocation());
206             });
207         }
208         for (Module mod : modSet.nonnullModule()) {
209             fillSource(requiredSources, mod.getName(), toYangCommon(mod.getRevision()), mod.getLocation());
210             mod.nonnullSubmodule().forEach(sub -> {
211                 fillSource(librarySources, sub.getName(), toYangCommon(sub.getRevision()), sub.getLocation());
212             });
213         }
214     }
215
216     private static void fillSource(final List<SourceReference> sources, final YangIdentifier sourceName,
217             final Optional<Revision> revision, final List<Uri> uris) {
218         final SourceIdentifier sourceId = RevisionSourceIdentifier.create(sourceName.getValue(), revision);
219         final SourceReference sourceRef;
220         if (uris != null && uris.isEmpty()) {
221             final List<URL> locations = new ArrayList<>();
222             for (Uri uri : uris) {
223                 try {
224                     locations.add(new URL(uri.getValue()));
225                 } catch (MalformedURLException e) {
226                     LOG.debug("Ignoring invalid schema location {}", uri, e);
227                 }
228             }
229             sourceRef = SourceReference.of(sourceId, locations);
230         } else {
231             sourceRef = SourceReference.of(sourceId);
232         }
233
234         sources.add(sourceRef);
235     }
236
237     private static Optional<Revision> toYangCommon(final @Nullable RevisionIdentifier revisionIdentifier) {
238         return revisionIdentifier == null ? Optional.empty() : Optional.of(Revision.of(revisionIdentifier.getValue()));
239     }
240
241 }