Eliminate YangModelDependencyInfo
[yangtools.git] / model / yang-model-spi / src / main / java / org / opendaylight / yangtools / yang / model / spi / source / SourceInfo.java
1 /*
2  * Copyright (c) 2024 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.yangtools.yang.model.spi.source;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableSet;
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.util.ArrayList;
15 import java.util.Comparator;
16 import org.eclipse.jdt.annotation.NonNullByDefault;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.yangtools.yang.common.Revision;
19 import org.opendaylight.yangtools.yang.common.UnresolvedQName.Unqualified;
20 import org.opendaylight.yangtools.yang.common.XMLNamespace;
21 import org.opendaylight.yangtools.yang.common.YangVersion;
22 import org.opendaylight.yangtools.yang.model.api.source.SourceDependency.BelongsTo;
23 import org.opendaylight.yangtools.yang.model.api.source.SourceDependency.Import;
24 import org.opendaylight.yangtools.yang.model.api.source.SourceDependency.Include;
25 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
26 import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;
27 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
29 import org.opendaylight.yangtools.yang.model.api.stmt.RevisionStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.RootDeclaredStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.YangVersionStatement;
32
33 /**
34  * Linkage information about a particular {@link SourceRepresentation}. It has two specializations
35  * <ol>
36  *   <li>{@link SourceInfo.Module} pertaining to {@link SourceRepresentation} which have {@code module} as its root
37  *       statement</li>
38  *   <li>{@link SourceInfo.Submodule} pertaining to {@link SourceRepresentation} which have {@code submodule} as its
39  *       root statement</li>
40  * </ol>
41  *
42  * <p>
43  * This interface captures the basic metadata needed for interpretation and linkage of the source, as represented by the
44  * following ABNF constructs placed at the start of a YANG file:
45  * <ul>
46  *   <li>{@code module-header-stmts} or {@code submodule-header-stmts}</li>
47  *   <li>{@code linkage-stmts}</li>
48  *   <li>{@code revision-stmts}<li>
49  * </ul>
50  */
51 @NonNullByDefault
52 public sealed interface SourceInfo permits SourceInfo.Module, SourceInfo.Submodule {
53     /**
54      * Return the {@link SourceIdentifier} of this source, as expressed by {@link RootDeclaredStatement#argument()}
55      * contract coupled with the first entry in {@link #revisions()}.
56      *
57      * @return name of this source.
58      */
59     SourceIdentifier sourceId();
60
61     /**
62      * Return {@link YangVersion} of the source. If no {@link YangVersionStatement} is present, this method will return
63      * {@link YangVersion#VERSION_1}.
64      *
65      * @return {@link YangVersion} of the source
66      */
67     YangVersion yangVersion();
68
69     /**
70      * The set of all {@link RevisionDateStatement} mentioned in {@link RevisionStatement}s. The returned set is ordered
71      * in reverse order, i.e. newest revision is encountered first.
72      *
73      * @return all revisions known by this source
74      */
75     ImmutableSet<Revision> revisions();
76
77     /**
78      * Return all {@link Import} dependencies.
79      *
80      * @return all import dependencies
81      */
82     ImmutableSet<Import> imports();
83
84     /**
85      * Return all {@link Include} dependencies.
86      *
87      * @return all include dependencies
88      */
89     ImmutableSet<Include> includes();
90
91     /**
92      * A {@link SourceInfo} about a {@link ModuleStatement}-backed source.
93      */
94     record Module(
95             SourceIdentifier sourceId,
96             YangVersion yangVersion,
97             XMLNamespace namespace,
98             Unqualified prefix,
99             ImmutableSet<Revision> revisions,
100             ImmutableSet<Import> imports,
101             ImmutableSet<Include> includes) implements SourceInfo {
102         public Module {
103             requireNonNull(sourceId);
104             requireNonNull(yangVersion);
105             requireNonNull(namespace);
106             requireNonNull(prefix);
107             requireNonNull(revisions);
108             requireNonNull(imports);
109             requireNonNull(includes);
110         }
111
112         public static Builder builder() {
113             return new Builder();
114         }
115
116         public static final class Builder extends SourceInfo.Builder<Builder, Module> {
117             @SuppressFBWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
118                 justification = "https://github.com/spotbugs/spotbugs/issues/743")
119             private @Nullable XMLNamespace namespace;
120             @SuppressFBWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
121                 justification = "https://github.com/spotbugs/spotbugs/issues/743")
122             private @Nullable Unqualified prefix;
123
124             Builder() {
125                 // Hidden on purpose
126             }
127
128             public Builder setNamespace(final XMLNamespace namespace) {
129                 this.namespace = requireNonNull(namespace);
130                 return this;
131             }
132
133             public Builder setPrefix(final Unqualified prefix) {
134                 this.prefix = requireNonNull(prefix);
135                 return this;
136             }
137
138             @Override
139             Module buildInstance(final SourceIdentifier sourceId, final YangVersion yangVersion,
140                     final ImmutableSet<Revision> revisions, final ImmutableSet<Import> imports,
141                     final ImmutableSet<Include> includes) {
142                 return new Module(sourceId, yangVersion, requireNonNull(namespace), requireNonNull(prefix), revisions,
143                     imports, includes);
144             }
145         }
146     }
147
148     /**
149      * A {@link SourceInfo} about a {@code submodule}.
150      */
151     record Submodule(
152             SourceIdentifier sourceId,
153             YangVersion yangVersion,
154             BelongsTo belongsTo,
155             ImmutableSet<Revision> revisions,
156             ImmutableSet<Import> imports,
157             ImmutableSet<Include> includes) implements SourceInfo {
158         public Submodule {
159             requireNonNull(sourceId);
160             requireNonNull(yangVersion);
161             requireNonNull(belongsTo);
162             requireNonNull(revisions);
163             requireNonNull(imports);
164             requireNonNull(imports);
165             requireNonNull(includes);
166         }
167
168         public static Builder builder() {
169             return new Builder();
170         }
171
172         public static final class Builder extends SourceInfo.Builder<Builder, Submodule> {
173             @SuppressFBWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
174                 justification = "https://github.com/spotbugs/spotbugs/issues/743")
175             private @Nullable BelongsTo belongsTo;
176
177             Builder() {
178                 // Hidden on purpose
179             }
180
181             public Builder setBelongsTo(final BelongsTo belongsTo) {
182                 this.belongsTo = requireNonNull(belongsTo);
183                 return this;
184             }
185
186             @Override
187             Submodule buildInstance(final SourceIdentifier sourceId, final YangVersion yangVersion,
188                     final ImmutableSet<Revision> revisions, final ImmutableSet<Import> imports,
189                     final ImmutableSet<Include> includes) {
190                 return new Submodule(sourceId, yangVersion, requireNonNull(belongsTo), revisions, imports, includes);
191             }
192         }
193     }
194
195     abstract sealed class Builder<B extends Builder<B, I>, I extends SourceInfo> {
196         private final ImmutableSet.Builder<Import> imports = ImmutableSet.builder();
197         private final ImmutableSet.Builder<Include> includes = ImmutableSet.builder();
198         private final ArrayList<Revision> revisions = new ArrayList<>();
199         private YangVersion yangVersion = YangVersion.VERSION_1;
200         @SuppressFBWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
201             justification = "https://github.com/spotbugs/spotbugs/issues/743")
202         private @Nullable Unqualified name;
203
204         public final B setName(final Unqualified newName) {
205             name = requireNonNull(newName);
206             return thisInstance();
207         }
208
209         public final B setYangVersion(final YangVersion newYangVersion) {
210             yangVersion = requireNonNull(newYangVersion);
211             return thisInstance();
212         }
213
214         public final B addImport(final Import importDep) {
215             imports.add(importDep);
216             return thisInstance();
217         }
218
219         public final B addInclude(final Include includeDep) {
220             includes.add(includeDep);
221             return thisInstance();
222         }
223
224         public final B addRevision(final Revision revision) {
225             revisions.add(revision);
226             return thisInstance();
227         }
228
229         public final I build() {
230             final var sorted = revisions.stream()
231                 .sorted(Comparator.reverseOrder())
232                 .collect(ImmutableSet.toImmutableSet());
233
234             return buildInstance(
235                 new SourceIdentifier(requireNonNull(name), sorted.isEmpty() ? null : sorted.iterator().next()),
236                 yangVersion, sorted, imports.build(), includes.build());
237         }
238
239         abstract I buildInstance(SourceIdentifier sourceId, YangVersion yangVersion, ImmutableSet<Revision> revisions,
240             ImmutableSet<Import> imports, ImmutableSet<Include> includes);
241
242         @SuppressWarnings("unchecked")
243         private B thisInstance() {
244             return (B) this;
245         }
246     }
247 }