YANGTOOLS-706: Split up base utility classes into rfc6020.util
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / rfc6020 / ImportStatementDefinition.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.parser.stmt.rfc6020;
9
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.SOURCE_LINKAGE;
12 import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.SOURCE_PRE_LINKAGE;
13 import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.findFirstDeclaredSubstatement;
14 import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf;
15
16 import com.google.common.base.MoreObjects.ToStringHelper;
17 import com.google.common.base.Verify;
18 import java.net.URI;
19 import java.util.Collection;
20 import java.util.Optional;
21 import org.opendaylight.yangtools.concepts.SemVer;
22 import org.opendaylight.yangtools.yang.common.Revision;
23 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
24 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
25 import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement;
29 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
30 import org.opendaylight.yangtools.yang.model.repo.api.SemVerSourceIdentifier;
31 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
32 import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace;
33 import org.opendaylight.yangtools.yang.parser.spi.PreLinkageModuleNamespace;
34 import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
35 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceContext;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionModuleNamespace;
42 import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionNamespace;
43 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
44 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
45 import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
46 import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace;
47 import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToModuleCtx;
48 import org.opendaylight.yangtools.yang.parser.spi.source.ImportPrefixToSemVerSourceIdentifier;
49 import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext;
50 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToSourceIdentifier;
51 import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToNamespace;
52 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
53 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ImportEffectiveStatementImpl;
54
55 public class ImportStatementDefinition extends
56         AbstractStatementSupport<String, ImportStatement, EffectiveStatement<String, ImportStatement>> {
57     private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator
58             .builder(YangStmtMapping.IMPORT)
59             .addMandatory(YangStmtMapping.PREFIX)
60             .addOptional(YangStmtMapping.REVISION_DATE)
61             .addOptional(SupportedExtensionsMapping.OPENCONFIG_VERSION)
62             .build();
63
64     public ImportStatementDefinition() {
65         super(YangStmtMapping.IMPORT);
66     }
67
68     @Override
69     public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
70         return value;
71     }
72
73     @Override
74     public ImportStatement createDeclared(final StmtContext<String, ImportStatement, ?> ctx) {
75         return new ImportStatementImpl(ctx);
76     }
77
78     @Override
79     public EffectiveStatement<String, ImportStatement> createEffective(
80             final StmtContext<String, ImportStatement, EffectiveStatement<String, ImportStatement>> ctx) {
81         return new ImportEffectiveStatementImpl(ctx);
82     }
83
84     @Override
85     public void onPreLinkageDeclared(final Mutable<String, ImportStatement,
86             EffectiveStatement<String, ImportStatement>> stmt) {
87         /*
88          * Add ModuleIdentifier of a module which is required by this module.
89          * Based on this information, required modules are searched from library
90          * sources.
91          */
92         stmt.addRequiredSource(RevisionImport.getImportedSourceIdentifier(stmt));
93
94         final String moduleName = stmt.getStatementArgument();
95         final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_PRE_LINKAGE);
96         final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt,
97                 PreLinkageModuleNamespace.class, moduleName, SOURCE_PRE_LINKAGE);
98         final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction
99                 .mutatesCtx(stmt.getRoot(), SOURCE_PRE_LINKAGE);
100
101         importAction.apply(new InferenceAction() {
102             @Override
103             public void apply(final InferenceContext ctx) {
104                 final StmtContext<?, ?, ?> importedModuleContext = imported.resolve(ctx);
105                 Verify.verify(moduleName.equals(importedModuleContext.getStatementArgument()));
106                 final URI importedModuleNamespace = importedModuleContext.getFromNamespace(ModuleNameToNamespace.class,
107                         moduleName);
108                 Verify.verifyNotNull(importedModuleNamespace);
109                 final String impPrefix = SourceException.throwIfNull(
110                     firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class),
111                     stmt.getStatementSourceReference(), "Missing prefix statement");
112
113                 stmt.addToNs(ImpPrefixToNamespace.class, impPrefix, importedModuleNamespace);
114             }
115
116             @Override
117             public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
118                 InferenceException.throwIf(failed.contains(imported), stmt.getStatementSourceReference(),
119                         "Imported module [%s] was not found.", moduleName);
120             }
121         });
122     }
123
124     @Override
125     public void onLinkageDeclared(
126             final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) {
127         if (stmt.isEnabledSemanticVersioning()) {
128             SemanticVersionImport.onLinkageDeclared(stmt);
129         } else {
130             RevisionImport.onLinkageDeclared(stmt);
131         }
132     }
133
134     @Override
135     protected SubstatementValidator getSubstatementValidator() {
136         return SUBSTATEMENT_VALIDATOR;
137     }
138
139     private static class RevisionImport {
140
141         private RevisionImport() {
142             throw new UnsupportedOperationException("Utility class");
143         }
144
145         private static void onLinkageDeclared(
146                 final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) {
147             final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_LINKAGE);
148             final Prerequisite<StmtContext<?, ?, ?>> imported;
149             final String moduleName = stmt.getStatementArgument();
150             final Revision revision = firstAttributeOf(stmt.declaredSubstatements(), RevisionDateStatement.class);
151             if (revision == null) {
152                 imported = importAction.requiresCtx(stmt, ModuleNamespace.class,
153                     NamespaceKeyCriterion.latestRevisionModule(moduleName), SOURCE_LINKAGE);
154             } else {
155                 imported = importAction.requiresCtx(stmt, ModuleNamespace.class,
156                     RevisionSourceIdentifier.create(moduleName, Optional.of(revision)), SOURCE_LINKAGE);
157             }
158
159             final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction.mutatesCtx(stmt.getRoot(),
160                 SOURCE_LINKAGE);
161
162             importAction.apply(new InferenceAction() {
163                 @Override
164                 public void apply(final InferenceContext ctx) {
165                     final StmtContext<?, ?, ?> importedModule = imported.resolve(ctx);
166
167                     linkageTarget.resolve(ctx).addToNs(ImportedModuleContext.class,
168                         stmt.getFromNamespace(ModuleCtxToSourceIdentifier.class, importedModule), importedModule);
169                     final String impPrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
170                     final URI modNs = firstAttributeOf(importedModule.declaredSubstatements(),
171                         NamespaceStatement.class);
172                     stmt.addToNs(ImportPrefixToModuleCtx.class, impPrefix, importedModule);
173                     stmt.addToNs(URIStringToImpPrefix.class, modNs.toString(), impPrefix);
174                 }
175
176                 @Override
177                 public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
178                     if (failed.contains(imported)) {
179                         throw new InferenceException(stmt.getStatementSourceReference(),
180                                 "Imported module [%s] was not found.", moduleName);
181                     }
182                 }
183             });
184         }
185
186         static SourceIdentifier getImportedSourceIdentifier(final StmtContext<String, ImportStatement, ?> stmt) {
187             final StmtContext<Revision, ?, ?> revision = findFirstDeclaredSubstatement(stmt,
188                 RevisionDateStatement.class);
189             return revision == null ? RevisionSourceIdentifier.create(stmt.getStatementArgument())
190                     : RevisionSourceIdentifier.create(stmt.getStatementArgument(), revision.getStatementArgument());
191         }
192     }
193
194     private static class SemanticVersionImport {
195
196         private abstract static class CompatibleCriterion extends NamespaceKeyCriterion<SemVerSourceIdentifier> {
197             private final String moduleName;
198
199             CompatibleCriterion(final String moduleName) {
200                 this.moduleName = requireNonNull(moduleName);
201             }
202
203             @Override
204             public boolean match(final SemVerSourceIdentifier key) {
205                 return moduleName.equals(key.getName());
206             }
207
208             @Override
209             protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
210                 return toStringHelper.add("moduleName", moduleName);
211             }
212         }
213
214         private static final class NoVerCompatibleCriterion extends CompatibleCriterion {
215             NoVerCompatibleCriterion(final String moduleName) {
216                 super(moduleName);
217             }
218
219             @Override
220             public SemVerSourceIdentifier select(final SemVerSourceIdentifier first,
221                     final SemVerSourceIdentifier second) {
222                 // TODO Auto-generated method stub
223                 return null;
224             }
225         }
226
227         private static final class SemVerCompatibleCriterion extends CompatibleCriterion {
228             private final SemVer semVer;
229
230             SemVerCompatibleCriterion(final String moduleName, final SemVer semVer) {
231                 super(moduleName);
232                 this.semVer = requireNonNull(semVer);
233             }
234
235             @Override
236             public boolean match(final SemVerSourceIdentifier key) {
237                 if (!super.match(key)) {
238                     return false;
239                 }
240                 final Optional<SemVer> optKeyVer = key.getSemanticVersion();
241                 if (!optKeyVer.isPresent()) {
242                     return false;
243                 }
244
245                 final SemVer keyVer = optKeyVer.get();
246                 if (semVer.getMajor() != keyVer.getMajor()) {
247                     return false;
248                 }
249                 if (semVer.getMinor() > keyVer.getMinor()) {
250                     return false;
251                 }
252                 return semVer.getMinor() < keyVer.getMinor() || semVer.getPatch() <= keyVer.getPatch();
253             }
254
255             @Override
256             public SemVerSourceIdentifier select(final SemVerSourceIdentifier first,
257                     final SemVerSourceIdentifier second) {
258                 return first.getSemanticVersion().get().compareTo(second.getSemanticVersion().get()) >= 0 ? first
259                         : second;
260             }
261
262             @Override
263             protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
264                 return super.addToStringAttributes(toStringHelper).add("version", semVer);
265             }
266         }
267
268
269         private SemanticVersionImport() {
270             throw new UnsupportedOperationException("Utility class");
271         }
272
273         private static void onLinkageDeclared(
274                 final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) {
275             final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_LINKAGE);
276             final String moduleName = stmt.getStatementArgument();
277             final SemVer semanticVersion = stmt.getFromNamespace(SemanticVersionNamespace.class, stmt);
278             final CompatibleCriterion criterion = semanticVersion == null ? new NoVerCompatibleCriterion(moduleName)
279                     : new SemVerCompatibleCriterion(moduleName, semanticVersion);
280
281             final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt,
282                 SemanticVersionModuleNamespace.class, criterion, SOURCE_LINKAGE);
283             final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction.mutatesCtx(stmt.getRoot(),
284                 SOURCE_LINKAGE);
285
286             importAction.apply(new InferenceAction() {
287                 @Override
288                 public void apply(final InferenceContext ctx) {
289                     final StmtContext<?, ?, ?> importedModule = imported.resolve(ctx);
290                     final SemVer importedVersion = stmt.getFromNamespace(SemanticVersionNamespace.class, stmt);
291                     final SourceIdentifier importedModuleIdentifier = importedModule.getFromNamespace(
292                         ModuleCtxToSourceIdentifier.class, importedModule);
293                     final SemVerSourceIdentifier semVerModuleIdentifier = createSemVerModuleIdentifier(
294                         importedModuleIdentifier, importedVersion);
295
296                     linkageTarget.resolve(ctx).addToNs(ImportedModuleContext.class, importedModuleIdentifier,
297                         importedModule);
298                     final String impPrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class);
299                     stmt.addToNs(ImportPrefixToModuleCtx.class, impPrefix, importedModule);
300                     stmt.addToNs(ImportPrefixToSemVerSourceIdentifier.class, impPrefix, semVerModuleIdentifier);
301
302                     final URI modNs = firstAttributeOf(importedModule.declaredSubstatements(),
303                         NamespaceStatement.class);
304                     stmt.addToNs(URIStringToImpPrefix.class, modNs.toString(), impPrefix);
305                 }
306
307                 @Override
308                 public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) {
309                     if (failed.contains(imported)) {
310                         throw new InferenceException(stmt.getStatementSourceReference(),
311                                 "Unable to find module compatible with requested import [%s(%s)].", moduleName,
312                                 getRequestedImportVersionString(stmt));
313                     }
314                 }
315             });
316         }
317
318         private static Optional<SemVer> getRequestedImportVersion(final StmtContext<?, ?, ?> stmt) {
319             return Optional.ofNullable(stmt.getFromNamespace(SemanticVersionNamespace.class, stmt));
320         }
321
322         private static String getRequestedImportVersionString(final StmtContext<?, ?, ?> stmt) {
323             return getRequestedImportVersion(stmt).map(SemVer::toString).orElse("<any>");
324         }
325
326         private static SemVerSourceIdentifier createSemVerModuleIdentifier(
327                 final SourceIdentifier importedModuleIdentifier, final SemVer semVer) {
328             return SemVerSourceIdentifier.create(importedModuleIdentifier.getName(),
329                 importedModuleIdentifier.getRevision(), semVer);
330         }
331     }
332 }