Remove getSemanticVersion()
[yangtools.git] / parser / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / repo / DependencyResolver.java
1 /*
2  * Copyright (c) 2014 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.repo;
9
10 import com.google.common.base.MoreObjects;
11 import com.google.common.collect.ArrayListMultimap;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.ImmutableMultimap;
14 import com.google.common.collect.Multimap;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Optional;
22 import java.util.Set;
23 import org.opendaylight.yangtools.yang.common.Revision;
24 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
25 import org.opendaylight.yangtools.yang.model.api.stmt.ImportEffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
27 import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration;
28 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * Inter-module dependency resolved. Given a set of schema source identifiers and their
34  * corresponding dependency information, the {@link #create(Map)} method creates a
35  * a view of how consistent the dependencies are. In particular, this detects whether
36  * any imports are unsatisfied.
37  */
38 // FIXME: improve this class to track and expose how wildcard imports were resolved.
39 //        That information will allow us to track "damage" to dependency resolution
40 //        as new models are added to a schema context.
41 abstract class DependencyResolver {
42     private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class);
43     private final ImmutableList<SourceIdentifier> resolvedSources;
44     private final ImmutableList<SourceIdentifier> unresolvedSources;
45     private final ImmutableMultimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
46
47     protected DependencyResolver(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
48         final Collection<SourceIdentifier> resolved = new ArrayList<>(depInfo.size());
49         final Collection<SourceIdentifier> pending = new ArrayList<>(depInfo.keySet());
50         final Map<SourceIdentifier, BelongsToDependency> submodules = new HashMap<>();
51
52         boolean progress;
53         do {
54             progress = false;
55
56             final Iterator<SourceIdentifier> it = pending.iterator();
57             while (it.hasNext()) {
58                 final SourceIdentifier id = it.next();
59                 final YangModelDependencyInfo dep = depInfo.get(id);
60
61                 boolean okay = true;
62
63                 final Set<ModuleImport> dependencies = dep.getDependencies();
64
65                 // in case of submodule, remember belongs to
66                 if (dep instanceof YangModelDependencyInfo.SubmoduleDependencyInfo) {
67                     final String parent = ((YangModelDependencyInfo.SubmoduleDependencyInfo) dep).getParentModule();
68                     submodules.put(id, new BelongsToDependency(parent));
69                 }
70
71                 for (final ModuleImport mi : dependencies) {
72                     if (!isKnown(resolved, mi)) {
73                         LOG.debug("Source {} is missing import {}", id, mi);
74                         okay = false;
75                         break;
76                     }
77                 }
78
79                 if (okay) {
80                     LOG.debug("Resolved source {}", id);
81                     resolved.add(id);
82                     it.remove();
83                     progress = true;
84                 }
85             }
86         } while (progress);
87
88         /// Additional check only for belongs-to statement
89         for (final Entry<SourceIdentifier, BelongsToDependency> submodule : submodules.entrySet()) {
90             final BelongsToDependency belongs = submodule.getValue();
91             final SourceIdentifier sourceIdentifier = submodule.getKey();
92             if (!isKnown(resolved, belongs)) {
93                 LOG.debug("Source {} is missing parent {}", sourceIdentifier, belongs);
94                 pending.add(sourceIdentifier);
95                 resolved.remove(sourceIdentifier);
96             }
97         }
98
99         final Multimap<SourceIdentifier, ModuleImport> imports = ArrayListMultimap.create();
100         for (final SourceIdentifier id : pending) {
101             final YangModelDependencyInfo dep = depInfo.get(id);
102             for (final ModuleImport mi : dep.getDependencies()) {
103                 if (!isKnown(pending, mi) && !isKnown(resolved, mi)) {
104                     imports.put(id, mi);
105                 }
106             }
107         }
108
109         this.resolvedSources = ImmutableList.copyOf(resolved);
110         this.unresolvedSources = ImmutableList.copyOf(pending);
111         this.unsatisfiedImports = ImmutableMultimap.copyOf(imports);
112     }
113
114     protected abstract boolean isKnown(Collection<SourceIdentifier> haystack, ModuleImport mi);
115
116     abstract YangParserConfiguration parserConfig();
117
118     /**
119      * Collection of sources which have been resolved.
120      */
121     Collection<SourceIdentifier> getResolvedSources() {
122         return resolvedSources;
123     }
124
125     /**
126      * Collection of sources which have not been resolved due to missing dependencies.
127      */
128     Collection<SourceIdentifier> getUnresolvedSources() {
129         return unresolvedSources;
130     }
131
132     /**
133      * Detailed information about which imports were missing. The key in the map
134      * is the source identifier of module which was issuing an import, the values
135      * are imports which were unsatisfied.
136      *
137      * <p>
138      * Note that this map contains only imports which are missing from the reactor,
139      * not transitive failures.
140      *
141      * <p>
142      * Examples:
143      * <ul><li>
144      * If A imports B, B imports C, and both A and B are in the reactor, only B->C
145      * will be reported.
146      * </li><li>
147      * If A imports B and C, B imports C, and both A and B are in the reactor,
148      * A->C and B->C will be reported.
149      * </li></ul>
150      */
151     Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
152         return unsatisfiedImports;
153     }
154
155     private static class BelongsToDependency implements ModuleImport {
156         private final String parent;
157
158         BelongsToDependency(final String parent) {
159             this.parent = parent;
160         }
161
162         @Override
163         public String getModuleName() {
164             return parent;
165         }
166
167         @Override
168         public Optional<Revision> getRevision() {
169             return Optional.empty();
170         }
171
172         @Override
173         public Optional<String> getDescription() {
174             return Optional.empty();
175         }
176
177         @Override
178         public Optional<String> getReference() {
179             return Optional.empty();
180         }
181
182         @Override
183         public String getPrefix() {
184             throw new UnsupportedOperationException();
185         }
186
187         @Override
188         public String toString() {
189             return MoreObjects.toStringHelper(this).add("parent", parent).toString();
190         }
191
192         @Override
193         public ImportEffectiveStatement asEffectiveStatement() {
194             throw new UnsupportedOperationException();
195         }
196     }
197 }