Standalone yang library data writer
[netconf.git] / apps / yanglib-mdsal-writer / src / main / java / org / opendaylight / netconf / yanglib / writer / YangLibraryContentBuilderUtil.java
1 /*
2  * Copyright (c) 2023 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.netconf.yanglib.writer;
9
10 import static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.Module.ConformanceType.Implement;
11 import static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.Module.ConformanceType.Import;
12
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableSet;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.Map;
18 import java.util.Optional;
19 import java.util.Set;
20 import java.util.stream.Collectors;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.datastores.rev180214.Operational;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesState;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesStateBuilder;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.RevisionIdentifier;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibrary;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibraryBuilder;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.CommonLeafs;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.module.Deviation;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.module.DeviationBuilder;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.module.DeviationKey;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.module.SubmoduleBuilder;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.DatastoreBuilder;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.ModuleSetBuilder;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.SchemaBuilder;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.YangIdentifier;
39 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
40 import org.opendaylight.yangtools.yang.common.QNameModule;
41 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
42 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
43 import org.opendaylight.yangtools.yang.model.api.Module;
44 import org.opendaylight.yangtools.yang.model.api.ModuleLike;
45
46 /**
47  * Utility class responsible for building ietf-yang-library content.
48  */
49 // TODO: current artifact is part of integration with YangLibrarySupport from MDSAL project,
50 //  it expected to be removed as extra once YangLibrarySupport is fully supporting required functionality.
51 //  https://jira.opendaylight.org/browse/MDSAL-833
52 //  https://jira.opendaylight.org/browse/MDSAL-835
53 final class YangLibraryContentBuilderUtil {
54     private static final CommonLeafs.Revision EMPTY_REVISION = new CommonLeafs.Revision("");
55
56     static final String DEFAULT_MODULE_SET_NAME = "ODL_modules";
57     static final String DEFAULT_SCHEMA_NAME = "ODL_schema";
58
59     private YangLibraryContentBuilderUtil() {
60         // utility class
61     }
62
63     /**
64      * Builds ietf-yang-library content based on model context.
65      *
66      * @param context effective model context
67      * @param urlProvider optional schema source URL provider
68      * @return content as YangLibrary object
69      */
70     static YangLibrary buildYangLibrary(final @NonNull EffectiveModelContext context,
71             final @NonNull String contentId, final @Nullable YangLibrarySchemaSourceUrlProvider urlProvider) {
72         final var deviationsMap = getDeviationsMap(context);
73         return new YangLibraryBuilder()
74             .setModuleSet(BindingMap.of(new ModuleSetBuilder()
75                 .setName(DEFAULT_MODULE_SET_NAME)
76                 .setModule(context.getModules().stream()
77                     .map(module -> buildModule(module, deviationsMap, urlProvider))
78                     .collect(BindingMap.toMap())
79                 )
80                 .build()))
81             .setSchema(BindingMap.of(new SchemaBuilder()
82                 .setName(DEFAULT_SCHEMA_NAME)
83                 .setModuleSet(Set.of(DEFAULT_MODULE_SET_NAME))
84                 .build()))
85             .setDatastore(BindingMap.of(new DatastoreBuilder()
86                 .setName(Operational.VALUE)
87                 .setSchema(DEFAULT_SCHEMA_NAME)
88                 .build()))
89             .setContentId(contentId)
90             .build();
91     }
92
93     private static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104
94         .module.set.parameters.@NonNull Module buildModule(final @NonNull Module module,
95             final @NonNull  Map<QNameModule, Set<Module>> deviationsMap,
96             final @Nullable YangLibrarySchemaSourceUrlProvider urlProvider) {
97         return new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library
98             .rev190104.module.set.parameters.ModuleBuilder()
99             .setName(buildModuleKeyName(module))
100             .setRevision(buildRevision(module))
101             .setNamespace(new Uri(module.getNamespace().toString()))
102             .setFeature(buildFeatures(module).orElse(null))
103             .setDeviation(buildDeviations(module, deviationsMap).orElse(null))
104             .setLocation(buildSchemaSourceUrl(module, urlProvider).map(Set::of).orElse(null))
105             .setSubmodule(module.getSubmodules().stream()
106                 .map(subModule -> new SubmoduleBuilder()
107                     .setName(buildModuleKeyName(subModule))
108                     .setRevision(buildRevision(subModule))
109                     .setLocation(buildSchemaSourceUrl(subModule, urlProvider).map(Set::of).orElse(null))
110                     .build())
111                 .collect(BindingMap.toMap()))
112             .build();
113     }
114
115     /**
116      * Builds ietf-yang-library legacy content based on model context.
117      *
118      * @param context effective model context
119      * @param urlProvider optional schema source URL provider
120      * @return content as ModulesState object
121      * @deprecated due to model update via RFC 8525, the functionality serves backward compatibility.
122      */
123     @Deprecated
124     static ModulesState buildModuleState(final @NonNull EffectiveModelContext context,
125             final @NonNull String moduleSetId, final @Nullable YangLibrarySchemaSourceUrlProvider urlProvider) {
126         final var deviationsMap = getDeviationsMap(context);
127         return new ModulesStateBuilder()
128             .setModule(context.getModules().stream()
129                 .map(module -> buildLegacyModule(module, deviationsMap, urlProvider))
130                 .collect(BindingMap.toMap()))
131             .setModuleSetId(moduleSetId)
132             .build();
133     }
134
135     private static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library
136         .rev190104.module.list.@NonNull Module buildLegacyModule(final @NonNull Module module,
137             final @NonNull  Map<QNameModule, Set<Module>> deviationsMap,
138             final @Nullable YangLibrarySchemaSourceUrlProvider urlProvider) {
139
140         return new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library
141             .rev190104.module.list.ModuleBuilder()
142             .setName(buildModuleKeyName(module))
143             .setRevision(buildLegacyRevision(module))
144             .setNamespace(new Uri(module.getNamespace().toString()))
145             .setFeature(buildFeatures(module).orElse(null))
146             .setSchema(buildSchemaSourceUrl(module, urlProvider).orElse(null))
147             .setConformanceType(hasDeviations(module) ? Implement : Import)
148             .setDeviation(buildLegacyDeviations(module, deviationsMap).orElse(null))
149             .setSubmodule(module.getSubmodules().stream()
150                 .map(subModule -> new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library
151                     .rev190104.module.list.module.SubmoduleBuilder()
152                     .setName(buildModuleKeyName(subModule))
153                     .setRevision(buildLegacyRevision(subModule))
154                     .setSchema(buildSchemaSourceUrl(subModule, urlProvider).orElse(null))
155                     .build())
156                 .collect(BindingMap.toMap()))
157             .build();
158     }
159
160     private static RevisionIdentifier buildRevision(final ModuleLike module) {
161         return module.getQNameModule().getRevision().map(rev -> new RevisionIdentifier(rev.toString())).orElse(null);
162     }
163
164     private static CommonLeafs.Revision buildLegacyRevision(final ModuleLike module) {
165         return module.getQNameModule().getRevision()
166             .map(rev -> new CommonLeafs.Revision(new RevisionIdentifier(rev.toString()))).orElse(EMPTY_REVISION);
167     }
168
169     private static YangIdentifier buildModuleKeyName(final ModuleLike module) {
170         return new YangIdentifier(module.getName()
171             + module.getQNameModule().getRevision().map(revision -> "_" + revision).orElse(""));
172     }
173
174     private static @NonNull Optional<Uri> buildSchemaSourceUrl(final @NonNull ModuleLike module,
175             final @Nullable YangLibrarySchemaSourceUrlProvider urlProvider) {
176         return urlProvider == null ? Optional.empty() :
177             urlProvider.getSchemaSourceUrl(DEFAULT_MODULE_SET_NAME, module.getName(),
178                 module.getRevision().orElse(null));
179     }
180
181     private static Optional<Set<YangIdentifier>> buildFeatures(final ModuleLike module) {
182         if (module.getFeatures() == null || module.getFeatures().isEmpty()) {
183             return Optional.empty();
184         }
185         final var namespace = module.getQNameModule();
186         final var features = module.getFeatures().stream()
187             .map(FeatureDefinition::getQName)
188             // ensure the features belong to same module
189             .filter(featureName -> namespace.equals(featureName.getModule()))
190             .map(featureName -> new YangIdentifier(featureName.getLocalName()))
191             .collect(Collectors.toUnmodifiableSet());
192         return features.isEmpty() ? Optional.empty() : Optional.of(features);
193     }
194
195     private static boolean hasDeviations(final Module module) {
196         return module.getDeviations() != null && !module.getDeviations().isEmpty();
197     }
198
199     private static Optional<Set<YangIdentifier>> buildDeviations(final Module module,
200             final Map<QNameModule, Set<Module>> deviationsMap) {
201         final var deviationModules = deviationsMap.get(module.getQNameModule());
202         if (deviationModules == null) {
203             return Optional.empty();
204         }
205         return Optional.of(deviationModules.stream()
206             .map(devModule -> new YangIdentifier(buildModuleKeyName(devModule)))
207             .collect(ImmutableSet.toImmutableSet()));
208     }
209
210     private static Optional<Map<DeviationKey, Deviation>> buildLegacyDeviations(final Module module,
211             final Map<QNameModule, Set<Module>> deviationsMap) {
212         final var deviationModules = deviationsMap.get(module.getQNameModule());
213         if (deviationModules == null) {
214             return Optional.empty();
215         }
216         return Optional.of(deviationModules.stream()
217             .map(devModule -> new DeviationBuilder()
218                 .setName(buildModuleKeyName(devModule))
219                 .setRevision(buildLegacyRevision(devModule))
220                 .build())
221             .collect(BindingMap.toMap()));
222     }
223
224     private static @NonNull Map<QNameModule, Set<Module>> getDeviationsMap(final EffectiveModelContext context) {
225         final var result = new HashMap<QNameModule, Set<Module>>();
226         for (final var module : context.getModules()) {
227             if (module.getDeviations() == null || module.getDeviations().isEmpty()) {
228                 continue;
229             }
230             for (final var deviation : module.getDeviations()) {
231                 final var targetQname = deviation.getTargetPath().lastNodeIdentifier().getModule();
232                 result.computeIfAbsent(targetQname, key -> new HashSet<>()).add(module);
233             }
234         }
235         return ImmutableMap.copyOf(result);
236     }
237 }