--- /dev/null
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.model.api.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * An exception identifying a problem detected at a particular YANG statement. Exposes {@link #sourceRef()} to that
+ * statement.
+ */
+public class StatementSourceException extends RuntimeException {
+ @java.io.Serial
+ private static final long serialVersionUID = 2L;
+
+ private final @NonNull StatementSourceReference sourceRef;
+
+ public StatementSourceException(final StatementSourceReference sourceRef, final String message) {
+ super(message);
+ this.sourceRef = requireNonNull(sourceRef);
+ }
+
+ public StatementSourceException(final StatementSourceReference sourceRef, final String message,
+ final Throwable cause) {
+ super(message, cause);
+ this.sourceRef = requireNonNull(sourceRef);
+ }
+
+ public StatementSourceException(final StatementSourceReference sourceRef, final String format,
+ final Object... args) {
+ super(format.formatted(args));
+ this.sourceRef = requireNonNull(sourceRef);
+ }
+
+ /**
+ * Return the reference to the source which caused this exception.
+ *
+ * @return the reference to the source which caused this exception
+ */
+ public final @NonNull StatementSourceReference sourceRef() {
+ return sourceRef;
+ }
+
+ @java.io.Serial
+ private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
+ throwNSE();
+ }
+
+ @java.io.Serial
+ private void readObjectNoData() throws ObjectStreamException {
+ throwNSE();
+ }
+
+ @java.io.Serial
+ private void writeObject(final ObjectOutputStream stream) throws IOException {
+ throwNSE();
+ }
+
+ protected final void throwNSE() throws NotSerializableException {
+ throw new NotSerializableException(getClass().getName());
+ }
+}
*/
package org.opendaylight.yangtools.yang.parser.repo;
-import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
-
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.FluentFuture;
import java.io.IOException;
import java.util.List;
-import java.util.Map;
import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.concurrent.FluentFutures;
import org.opendaylight.yangtools.yang.ir.YangIRSchemaSource;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
-import org.opendaylight.yangtools.yang.parser.api.YangParser;
import org.opendaylight.yangtools.yang.parser.api.YangParserException;
import org.opendaylight.yangtools.yang.parser.api.YangParserFactory;
import org.opendaylight.yangtools.yang.parser.api.YangSyntaxErrorException;
}
@Override
- public FluentFuture<EffectiveModelContext> apply(final List<YangIRSchemaSource> sources)
- throws SchemaResolutionException, ReactorException {
- final Map<SourceIdentifier, YangIRSchemaSource> srcs = Maps.uniqueIndex(sources, getIdentifier);
- final Map<SourceIdentifier, YangModelDependencyInfo> deps =
- Maps.transformValues(srcs, YangModelDependencyInfo::forIR);
-
+ public FluentFuture<EffectiveModelContext> apply(final List<YangIRSchemaSource> sources) {
+ final var srcs = Maps.uniqueIndex(sources, getIdentifier);
+ final var deps = Maps.transformValues(srcs, YangModelDependencyInfo::forIR);
LOG.debug("Resolving dependency reactor {}", deps);
- final DependencyResolver res = switch (config.getStatementParserMode()) {
+ final var res = switch (config.getStatementParserMode()) {
case DEFAULT_MODE -> RevisionDependencyResolver.create(deps);
};
- if (!res.getUnresolvedSources().isEmpty()) {
- LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(),
- res.getUnsatisfiedImports());
- throw new SchemaResolutionException("Failed to resolve required models",
- res.getResolvedSources(), res.getUnsatisfiedImports());
+ final var unresolved = res.unresolvedSources();
+ if (!unresolved.isEmpty()) {
+ LOG.debug("Omitting models {} due to unsatisfied imports {}", unresolved, res.unsatisfiedImports());
+ return FluentFutures.immediateFailedFluentFuture(
+ new SchemaResolutionException("Failed to resolve required models", unresolved.get(0),
+ res.resolvedSources(), res.unsatisfiedImports()));
}
- final YangParser parser = parserFactory.createParser(res.parserConfig());
+ final var parser = parserFactory.createParser(res.parserConfig());
config.getSupportedFeatures().ifPresent(parser::setSupportedFeatures);
config.getModulesDeviatedByModules().ifPresent(parser::setModulesWithSupportedDeviations);
parser.addSource(entry.getValue());
} catch (YangSyntaxErrorException | IOException e) {
final var sourceId = entry.getKey();
- throw new SchemaResolutionException("Failed to add source " + sourceId, sourceId, e);
+ return FluentFutures.immediateFailedFluentFuture(
+ new SchemaResolutionException("Failed to add source " + sourceId, sourceId, e));
}
}
try {
schemaContext = parser.buildEffectiveModel();
} catch (final YangParserException e) {
- if (e.getCause() instanceof ReactorException re) {
- throw new SchemaResolutionException("Failed to resolve required models", re.getSourceIdentifier(), e);
- }
- throw new SchemaResolutionException("Failed to resolve required models", e);
+ return FluentFutures.immediateFailedFluentFuture(e.getCause() instanceof ReactorException re
+ ? new SchemaResolutionException("Failed to resolve required models", re.getSourceIdentifier(), re) : e);
}
- return immediateFluentFuture(schemaContext);
+ return FluentFutures.immediateFluentFuture(schemaContext);
}
}
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Optional;
-import java.util.Set;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.UnresolvedQName.Unqualified;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
import org.opendaylight.yangtools.yang.model.api.stmt.ImportEffectiveStatement;
import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration;
import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo;
+import org.opendaylight.yangtools.yang.parser.rfc7950.repo.YangModelDependencyInfo.SubmoduleDependencyInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// as new models are added to a schema context.
abstract class DependencyResolver {
private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class);
+
private final ImmutableList<SourceIdentifier> resolvedSources;
private final ImmutableList<SourceIdentifier> unresolvedSources;
private final ImmutableMultimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
protected DependencyResolver(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
- final Collection<SourceIdentifier> resolved = new ArrayList<>(depInfo.size());
- final Collection<SourceIdentifier> pending = new ArrayList<>(depInfo.keySet());
- final Map<SourceIdentifier, BelongsToDependency> submodules = new HashMap<>();
+ final var resolved = new ArrayList<SourceIdentifier>(depInfo.size());
+ final var pending = new ArrayList<>(depInfo.keySet());
+ final var submodules = new HashMap<SourceIdentifier, BelongsToDependency>();
boolean progress;
do {
progress = false;
- final Iterator<SourceIdentifier> it = pending.iterator();
+ final var it = pending.iterator();
while (it.hasNext()) {
- final SourceIdentifier id = it.next();
- final YangModelDependencyInfo dep = depInfo.get(id);
-
- boolean okay = true;
-
- final Set<ModuleImport> dependencies = dep.getDependencies();
+ final var sourceId = it.next();
+ final var dep = depInfo.get(sourceId);
// in case of submodule, remember belongs to
- if (dep instanceof YangModelDependencyInfo.SubmoduleDependencyInfo) {
- final var parent = ((YangModelDependencyInfo.SubmoduleDependencyInfo) dep).getParentModule();
- submodules.put(id, new BelongsToDependency(parent));
+ if (dep instanceof SubmoduleDependencyInfo submodule) {
+ final var parent = submodule.getParentModule();
+ submodules.put(sourceId, new BelongsToDependency(parent));
}
- for (final ModuleImport mi : dependencies) {
- if (!isKnown(resolved, mi)) {
- LOG.debug("Source {} is missing import {}", id, mi);
+ boolean okay = true;
+ for (var dependency : dep.getDependencies()) {
+ if (!isKnown(resolved, dependency)) {
+ LOG.debug("Source {} is missing import {}", sourceId, dependency);
okay = false;
break;
}
}
if (okay) {
- LOG.debug("Resolved source {}", id);
- resolved.add(id);
+ LOG.debug("Resolved source {}", sourceId);
+ resolved.add(sourceId);
it.remove();
progress = true;
}
} while (progress);
/// Additional check only for belongs-to statement
- for (final Entry<SourceIdentifier, BelongsToDependency> submodule : submodules.entrySet()) {
- final BelongsToDependency belongs = submodule.getValue();
- final SourceIdentifier sourceIdentifier = submodule.getKey();
+ for (var submodule : submodules.entrySet()) {
+ final var sourceId = submodule.getKey();
+ final var belongs = submodule.getValue();
if (!isKnown(resolved, belongs)) {
- LOG.debug("Source {} is missing parent {}", sourceIdentifier, belongs);
- pending.add(sourceIdentifier);
- resolved.remove(sourceIdentifier);
+ LOG.debug("Source {} is missing parent {}", sourceId, belongs);
+ pending.add(sourceId);
+ resolved.remove(sourceId);
}
}
- final Multimap<SourceIdentifier, ModuleImport> imports = ArrayListMultimap.create();
- for (final SourceIdentifier id : pending) {
- final YangModelDependencyInfo dep = depInfo.get(id);
- for (final ModuleImport mi : dep.getDependencies()) {
- if (!isKnown(pending, mi) && !isKnown(resolved, mi)) {
- imports.put(id, mi);
+ final var imports = ArrayListMultimap.<SourceIdentifier, ModuleImport>create();
+ for (var sourceId : pending) {
+ for (var dependency : depInfo.get(sourceId).getDependencies()) {
+ if (!isKnown(pending, dependency) && !isKnown(resolved, dependency)) {
+ imports.put(sourceId, dependency);
}
}
}
/**
* Collection of sources which have been resolved.
*/
- Collection<SourceIdentifier> getResolvedSources() {
+ ImmutableList<SourceIdentifier> resolvedSources() {
return resolvedSources;
}
/**
* Collection of sources which have not been resolved due to missing dependencies.
*/
- Collection<SourceIdentifier> getUnresolvedSources() {
+ ImmutableList<SourceIdentifier> unresolvedSources() {
return unresolvedSources;
}
* A->C and B->C will be reported.
* </li></ul>
*/
- Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
+ ImmutableMultimap<SourceIdentifier, ModuleImport> unsatisfiedImports() {
return unsatisfiedImports;
}
}
@Beta
- public EffectiveModelContext trySchemaContext() throws SchemaResolutionException {
+ public EffectiveModelContext trySchemaContext() throws SchemaResolutionException, ExecutionException {
return trySchemaContext(StatementParserMode.DEFAULT_MODE);
}
@Beta
@SuppressWarnings("checkstyle:avoidHidingCauseException")
public EffectiveModelContext trySchemaContext(final StatementParserMode statementParserMode)
- throws SchemaResolutionException {
+ throws SchemaResolutionException, ExecutionException {
final var future = repository
.createEffectiveModelContextFactory(config(statementParserMode, getSupportedFeatures()))
.createEffectiveModelContext(ImmutableSet.copyOf(requiredSources));
} catch (InterruptedException e) {
throw new IllegalStateException("Interrupted while waiting for SchemaContext assembly", e);
} catch (ExecutionException e) {
- final var cause = e.getCause();
- if (cause instanceof SchemaResolutionException resolutionException) {
- throw resolutionException;
+ if (e.getCause() instanceof SchemaResolutionException sre) {
+ throw sre;
}
- throw new SchemaResolutionException("Failed to assemble SchemaContext", e);
+ throw e;
}
}
addToMap(map, "/no-revision/top@2012-10-10.yang");
final var resolved = RevisionDependencyResolver.create(map);
- assertEquals(0, resolved.getUnresolvedSources().size());
- assertEquals(0, resolved.getUnsatisfiedImports().size());
+ assertEquals(0, resolved.unresolvedSources().size());
+ assertEquals(0, resolved.unsatisfiedImports().size());
}
@Test
addToMap(map, "/model/baz.yang");
final var resolved = RevisionDependencyResolver.create(map);
- assertEquals(2, resolved.getResolvedSources().size());
- assertEquals(1, resolved.getUnresolvedSources().size());
- assertEquals(0, resolved.getUnsatisfiedImports().size());
+ assertEquals(2, resolved.resolvedSources().size());
+ assertEquals(1, resolved.unresolvedSources().size());
+ assertEquals(0, resolved.unsatisfiedImports().size());
}
@Test
addToMap(map, "/model/baz.yang");
final var resolved = RevisionDependencyResolver.create(map);
- assertEquals(0, resolved.getUnresolvedSources().size());
- assertEquals(0, resolved.getUnsatisfiedImports().size());
- assertEquals(4, resolved.getResolvedSources().size());
+ assertEquals(0, resolved.unresolvedSources().size());
+ assertEquals(0, resolved.unsatisfiedImports().size());
+ assertEquals(4, resolved.resolvedSources().size());
}
private static void addToMap(final Map<SourceIdentifier, YangModelDependencyInfo> map, final String yangFileName)
@Test
void testInvalidLeftParent() {
final var ex = assertThrows(SourceException.class, () -> parser.parseExpression(ctx, "foo("));
- assertSame(ref, ex.getSourceReference());
+ assertSame(ref, ex.sourceRef());
assertThat(ex.getMessage(), allOf(
startsWith("extraneous input '(' expecting "),
containsString(" at 1:3 [at ")));
@Test
void testInvalidRightParent() {
final var ex = assertThrows(SourceException.class, () -> parser.parseExpression(ctx, "foo)"));
- assertSame(ref, ex.getSourceReference());
+ assertSame(ref, ex.sourceRef());
assertThat(ex.getMessage(), allOf(
startsWith("extraneous input ')' expecting "),
containsString(" at 1:3 [at ")));
@Test
void testInvalidIdentifier() {
final var ex = assertThrows(SourceException.class, () -> parser.parseExpression(ctx, "foo%"));
- assertSame(ref, ex.getSourceReference());
+ assertSame(ref, ex.sourceRef());
assertThat(ex.getMessage(), startsWith("token recognition error at: '%' at 1:3 [at "));
}
import static java.util.Objects.requireNonNull;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSourceException;
import org.opendaylight.yangtools.yang.model.api.meta.StatementSourceReference;
import org.opendaylight.yangtools.yang.parser.spi.meta.CommonStmtCtx;
/**
* Thrown to indicate error in YANG model source.
*/
-public class SourceException extends RuntimeException {
+public class SourceException extends StatementSourceException {
+ @java.io.Serial
private static final long serialVersionUID = 1L;
- @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Interface-specified member")
- private final @NonNull StatementSourceReference sourceRef;
-
/**
* Create a new instance with the specified message and source. The message will be appended with
* the source reference.
*
* @param message Context message
- * @param source Statement source
+ * @param sourceRef Statement source
*/
- public SourceException(final @NonNull String message, final @NonNull StatementSourceReference source) {
- super(createMessage(message, source));
- sourceRef = source;
+ public SourceException(final @NonNull String message, final @NonNull StatementSourceReference sourceRef) {
+ super(sourceRef, createMessage(message, sourceRef));
}
/**
* the source reference.
*
* @param message Context message
- * @param source Statement source
+ * @param sourceRef Statement source
* @param cause Underlying cause of this exception
*/
- public SourceException(final @NonNull String message, final @NonNull StatementSourceReference source,
+ public SourceException(final @NonNull String message, final @NonNull StatementSourceReference sourceRef,
final Throwable cause) {
- super(createMessage(message, source), cause);
- sourceRef = source;
+ super(sourceRef, createMessage(message, sourceRef), cause);
}
/**
this(stmt.sourceReference(), cause, format, args);
}
- /**
- * Return the reference to the source which caused this exception.
- *
- * @return Source reference
- */
- public @NonNull StatementSourceReference getSourceReference() {
- return sourceRef;
- }
-
/**
* Throw an instance of this exception if an expression evaluates to true. If the expression evaluates to false,
* this method does nothing.
private final @NonNull ImmutableMultimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
private final @NonNull ImmutableList<SourceIdentifier> resolvedSources;
- public SchemaResolutionException(final @NonNull String message) {
- this(message, null);
- }
-
- public SchemaResolutionException(final @NonNull String message, final Throwable cause) {
- this(message, null, cause, ImmutableList.of(), ImmutableMultimap.of());
- }
-
public SchemaResolutionException(final @NonNull String message, final SourceIdentifier failedSource,
final Throwable cause) {
this(message, failedSource, cause, ImmutableList.of(), ImmutableMultimap.of());
}
- public SchemaResolutionException(final @NonNull String message,
+ public SchemaResolutionException(final @NonNull String message, final SourceIdentifier failedSource,
final @NonNull Collection<SourceIdentifier> resolvedSources,
final @NonNull Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
- this(message, null, null, resolvedSources, unsatisfiedImports);
+ this(message, failedSource, null, resolvedSources, unsatisfiedImports);
}
public SchemaResolutionException(final @NonNull String message, final SourceIdentifier failedSource,