package org.opendaylight.yangtools.yang.parser.impl;
import com.google.common.collect.Sets;
+
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.Set;
+
import org.antlr.v4.runtime.tree.ParseTree;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Anyxml_stmtContext;
* This validator expects only one module or submodule per file and performs
* only basic validation where context from all yang models is not present.
*/
-final class YangModelBasicValidationListener extends YangParserBaseListener {
+public final class YangModelBasicValidationListener extends YangParserBaseListener {
private static final Logger LOGGER = LoggerFactory.getLogger(YangModelBasicValidationListener.class);
private final Set<String> uniquePrefixes = new HashSet<>();
private final Set<String> uniqueImports = new HashSet<>();
import com.google.common.base.Splitter;
import com.google.common.collect.HashBiMap;
import com.google.common.io.ByteSource;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
+
import javax.annotation.concurrent.Immutable;
+
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
}
@Override
- public SchemaContext parseFile(final File yangFile, final File directory) throws IOException,
- YangSyntaxErrorException {
+ public SchemaContext parseFile(final File yangFile, final File directory) throws IOException, YangSyntaxErrorException {
Preconditions.checkState(yangFile.exists(), yangFile + " does not exists");
Preconditions.checkState(directory.exists(), directory + " does not exists");
Preconditions.checkState(directory.isDirectory(), directory + " is not a directory");
}
@Override
- public SchemaContext parseSources(final Collection<ByteSource> sources) throws IOException,
- YangSyntaxErrorException {
- Collection<Module> unsorted = parseYangModelSources(sources).values();
- Set<Module> sorted = new LinkedHashSet<>(
- ModuleDependencySort.sort(unsorted.toArray(new Module[unsorted.size()])));
- return resolveSchemaContext(sorted);
+ public SchemaContext parseSources(final Collection<ByteSource> sources) throws IOException,YangSyntaxErrorException {
+ return assembleContext(parseYangModelSources(sources).values());
}
@Override
return resolveSchemaContext(result);
}
- private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> resolveModulesWithImports(final List<ModuleBuilder> sorted,
+ private static LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> resolveModulesWithImports(final List<ModuleBuilder> sorted,
final SchemaContext context) {
final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = orderModules(sorted);
for (ModuleBuilder module : sorted) {
return new SchemaContextImpl(modules, identifiersToSources);
}
- private Map<ByteSource, Module> parseYangModelSources(final Collection<ByteSource> sources) throws IOException,
- YangSyntaxErrorException {
+ public Collection<Module> buildModules(final Collection<ModuleBuilder> builders) {
+ List<ModuleBuilder> sorted = ModuleDependencySort.sort(builders);
+ Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModulesWithImports(sorted, null);
+ Map<ModuleBuilder, Module> builderToModule = build(modules);
+
+ return builderToModule.values();
+ }
+
+ public SchemaContext assembleContext(final Collection<Module> modules) {
+ final Set<Module> sorted = new LinkedHashSet<>(
+ ModuleDependencySort.sort(modules.toArray(new Module[modules.size()])));
+ return resolveSchemaContext(sorted);
+ }
+
+ private Map<ByteSource, Module> parseYangModelSources(final Collection<ByteSource> sources) throws IOException, YangSyntaxErrorException {
if (sources == null || sources.isEmpty()) {
return Collections.emptyMap();
}
* @throws YangSyntaxErrorException
*/
// TODO: remove ByteSource result after removing YangModelParser
- private Map<ByteSource, ModuleBuilder> resolveSources(final Collection<ByteSource> streams) throws IOException,
- YangSyntaxErrorException {
+ private Map<ByteSource, ModuleBuilder> resolveSources(final Collection<ByteSource> streams) throws IOException, YangSyntaxErrorException {
Map<ByteSource, ModuleBuilder> builders = parseSourcesToBuilders(streams);
return resolveSubmodules(builders);
}
* topologically sorted modules
* @return modules ordered by name and revision
*/
- private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(final List<ModuleBuilder> modules) {
+ private static LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(final List<ModuleBuilder> modules) {
final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> result = new LinkedHashMap<>();
for (final ModuleBuilder builder : modules) {
if (builder == null) {
}
dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
}
-
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. 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.parser.repo;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Inter-module dependency resolved. Given a set of schema source identifiers and their
+ * corresponding dependency information, the {@link #create(Map)} method creates a
+ * a view of how consistent the dependencies are. In particular, this detects whether
+ * any imports are unsatisfied.
+ *
+ * FIXME: improve this class to track and expose how wildcard imports were resolved.
+ * That information will allow us to track "damage" to dependency resolution
+ * as new models are added to a schema context.
+ */
+final class DependencyResolver {
+ private static final Logger LOG = LoggerFactory.getLogger(DependencyResolver.class);
+ private final Collection<SourceIdentifier> resolvedSources;
+ private final Collection<SourceIdentifier> unresolvedSources;
+ private final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports;
+
+ public DependencyResolver(final Collection<SourceIdentifier> resolvedSources,
+ final Collection<SourceIdentifier> unresolvedSources, final Multimap<SourceIdentifier, ModuleImport> unsatisfiedImports) {
+ this.resolvedSources = Preconditions.checkNotNull(resolvedSources);
+ this.unresolvedSources = Preconditions.checkNotNull(unresolvedSources);
+ this.unsatisfiedImports = Preconditions.checkNotNull(unsatisfiedImports);
+ }
+
+ private static SourceIdentifier findWildcard(final Iterable<SourceIdentifier> haystack, final String needle) {
+ for (SourceIdentifier r : haystack) {
+ if (r.getName().equals(needle)) {
+ return r;
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean isKnown(final Collection<SourceIdentifier> haystack, final ModuleImport mi) {
+ final String rev = mi.getRevision() != null ? mi.getRevision().toString() : null;
+ final SourceIdentifier msi = SourceIdentifier.create(mi.getModuleName(), Optional.fromNullable(rev));
+
+ // Quick lookup
+ if (haystack.contains(msi)) {
+ return true;
+ }
+
+ // Slow revision-less walk
+ return rev == null && findWildcard(haystack, mi.getModuleName()) != null;
+ }
+
+ public static final DependencyResolver create(final Map<SourceIdentifier, YangModelDependencyInfo> depInfo) {
+ final Collection<SourceIdentifier> resolved = new ArrayList<>(depInfo.size());
+ final Collection<SourceIdentifier> pending = new ArrayList<>(depInfo.keySet());
+
+ boolean progress;
+ do {
+ progress = false;
+
+ final Iterator<SourceIdentifier> it = pending.iterator();
+ while (it.hasNext()) {
+ final SourceIdentifier id = it.next();
+ final YangModelDependencyInfo dep = depInfo.get(id);
+
+ boolean okay = true;
+ for (ModuleImport mi : dep.getDependencies()) {
+ if (!isKnown(resolved, mi)) {
+ LOG.debug("Source {} is missing import {}", id, mi);
+ okay = false;
+ break;
+ }
+ }
+
+ if (okay) {
+ LOG.debug("Resolved source {}", id);
+ resolved.add(id);
+ it.remove();
+ progress = true;
+ }
+ }
+ } while (progress);
+
+ if (!pending.isEmpty()) {
+ final Multimap<SourceIdentifier, ModuleImport> imports = ArrayListMultimap.create();
+ for (SourceIdentifier id : pending) {
+ final YangModelDependencyInfo dep = depInfo.get(id);
+ for (ModuleImport mi : dep.getDependencies()) {
+ if (!isKnown(pending, mi) && !isKnown(resolved, mi)) {
+ imports.put(id, mi);
+ }
+ }
+ }
+
+ return new DependencyResolver(resolved, pending, imports);
+ } else {
+ return new DependencyResolver(resolved, Collections.<SourceIdentifier>emptyList(), ImmutableMultimap.<SourceIdentifier, ModuleImport>of());
+ }
+ }
+
+ /**
+ * Collection of sources which have been resolved.
+ *
+ * @return
+ */
+ Collection<SourceIdentifier> getResolvedSources() {
+ return resolvedSources;
+ }
+
+ /**
+ * Collection of sources which have not been resolved due to missing dependencies.
+ *
+ * @return
+ */
+ Collection<SourceIdentifier> getUnresolvedSources() {
+ return unresolvedSources;
+ }
+
+ /**
+ * Detailed information about which imports were missing. The key in the map
+ * is the source identifier of module which was issuing an import, the values
+ * are imports which were unsatisfied.
+ *
+ * Note that this map contains only imports which are missing from the reactor,
+ * not transitive failures.
+ *
+ * Examples:
+ *
+ * If A imports B, B imports C, and both A and B are in the reactor, only B->C
+ * will be reported.
+ *
+ * If A imports B and C, B imports C, and both A and B are in the reactor,
+ * A->C and B->C will be reported.
+ *
+ * @return
+ */
+ Multimap<SourceIdentifier, ModuleImport> getUnsatisfiedImports() {
+ return unsatisfiedImports;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. 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.parser.repo;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Maps.EntryTransformer;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
+import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ThreadSafe
+public class URLSchemaContextResolver {
+ private static final Logger LOG = LoggerFactory.getLogger(URLSchemaContextResolver.class);
+ private static final Function<ASTSchemaSource, YangModelDependencyInfo> EXTRACT_DEPINFO = new Function<ASTSchemaSource, YangModelDependencyInfo>() {
+ @Override
+ public YangModelDependencyInfo apply(final ASTSchemaSource input) {
+ return input.getDependencyInformation();
+ }
+ };
+ private static final EntryTransformer<SourceIdentifier, Collection<YangModelDependencyInfo>, YangModelDependencyInfo> SQUASH_DEPINFO =
+ new EntryTransformer<SourceIdentifier, Collection<YangModelDependencyInfo>, YangModelDependencyInfo>() {
+ @Override
+ public YangModelDependencyInfo transformEntry(final SourceIdentifier key, final Collection<YangModelDependencyInfo> value) {
+ // FIXME: validate that all the info objects are the same
+ return value.iterator().next();
+ }
+ };
+ private static final Function<ASTSchemaSource, ParserRuleContext> EXTRACT_AST = new Function<ASTSchemaSource, ParserRuleContext>() {
+ @Override
+ public ParserRuleContext apply(final ASTSchemaSource input) {
+ return input.getAST();
+ }
+ };
+ private static final EntryTransformer<SourceIdentifier, Collection<ParserRuleContext>, ParserRuleContext> SQUASH_AST =
+ new EntryTransformer<SourceIdentifier, Collection<ParserRuleContext>, ParserRuleContext>() {
+ @Override
+ public ParserRuleContext transformEntry(final SourceIdentifier key, final Collection<ParserRuleContext> value) {
+ // FIXME: validate that all the info objects are the same
+ return value.iterator().next();
+ }
+ };
+
+ @GuardedBy("this")
+ private final Multimap<SourceIdentifier, ASTSchemaSource> resolvedRegs = ArrayListMultimap.create();
+ private final AtomicReference<Optional<SchemaContext>> currentSchemaContext = new AtomicReference<>(Optional.<SchemaContext>absent());
+ private final Queue<URLRegistration> outstandingRegs = new ConcurrentLinkedQueue<>();
+ private final TextToASTTransformer transformer;
+ @GuardedBy("this")
+ private Object version = new Object();
+ @GuardedBy("this")
+ private Object contextVersion = version;
+
+ private final class URLRegistration extends AbstractObjectRegistration<URL> {
+ @GuardedBy("this")
+ private CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> future;
+ @GuardedBy("this")
+ private ASTSchemaSource result;
+
+ protected URLRegistration(final URL url, final CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> future) {
+ super(url);
+ this.future = Preconditions.checkNotNull(future);
+ }
+
+ private synchronized boolean setResult(final ASTSchemaSource result) {
+ if (future != null) {
+ this.result = result;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void removeRegistration() {
+ // Cancel the future, but it may already be completing
+ future.cancel(false);
+
+ synchronized (this) {
+ future = null;
+ outstandingRegs.remove(this);
+ if (result != null) {
+ removeSchemaSource(result);
+ }
+ }
+ }
+ }
+
+ private URLSchemaContextResolver(final TextToASTTransformer transformer) {
+ this.transformer = Preconditions.checkNotNull(transformer);
+ }
+
+ public static URLSchemaContextResolver create(final String name) {
+ final ThreadFactory f = new ThreadFactoryBuilder().setDaemon(true).setNameFormat(name + "yangparser-%d").build();
+ final ListeningExecutorService s = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(f));
+
+ return new URLSchemaContextResolver(TextToASTTransformer.create(s));
+ }
+
+ /**
+ * Register a URL hosting a YANG Text file.
+ *
+ * @param url URL
+ */
+ public ObjectRegistration<URL> registerSource(final URL url) {
+ checkArgument(url != null, "Supplied URL must not be null");
+
+ final SourceIdentifier id = SourceIdentifier.create(url.getFile().toString(), Optional.<String>absent());
+ final YangTextSchemaSource text = new YangTextSchemaSource(id) {
+ @Override
+ public InputStream openStream() throws IOException {
+ return url.openStream();
+ }
+
+ @Override
+ protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+ return toStringHelper.add("url", url);
+ }
+ };
+
+ final CheckedFuture<ASTSchemaSource, SchemaSourceTransformationException> ast = transformer.transformSchemaSource(text);
+ final URLRegistration reg = new URLRegistration(url, ast);
+ outstandingRegs.add(reg);
+
+ Futures.addCallback(ast, new FutureCallback<ASTSchemaSource>() {
+ @Override
+ public void onSuccess(final ASTSchemaSource result) {
+ LOG.trace("Resolved URL {} to source {}", url, result);
+
+ outstandingRegs.remove(reg);
+ if (reg.setResult(result)) {
+ addSchemaSource(result);
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ LOG.warn("Failed to parse YANG text from {}, ignoring it", url, t);
+ outstandingRegs.remove(reg);
+ }
+ });
+
+ return reg;
+ }
+
+ private synchronized void addSchemaSource(final ASTSchemaSource src) {
+ resolvedRegs.put(src.getIdentifier(), src);
+ version = new Object();
+ }
+
+ private synchronized void removeSchemaSource(final ASTSchemaSource src) {
+ resolvedRegs.put(src.getIdentifier(), src);
+ version = new Object();
+ }
+
+ /**
+ * Try to parse all currently available yang files and build new schema context.
+ * @return new schema context iif there is at least 1 yang file registered and
+ * new schema context was successfully built.
+ */
+ public Optional<SchemaContext> getSchemaContext() {
+ while (true) {
+ Optional<SchemaContext> result;
+ final Multimap<SourceIdentifier, ASTSchemaSource> sources;
+ final Object v;
+ synchronized (this) {
+ result = currentSchemaContext.get();
+ if (version == contextVersion) {
+ return result;
+ }
+
+ sources = ImmutableMultimap.copyOf(resolvedRegs);
+ v = version;
+ }
+
+ if (!sources.isEmpty()) {
+ final Map<SourceIdentifier, YangModelDependencyInfo> deps =
+ Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_DEPINFO).asMap(), SQUASH_DEPINFO);
+
+ LOG.debug("Resolving dependency reactor {}", deps);
+ final DependencyResolver res = DependencyResolver.create(deps);
+ if (!res.getUnresolvedSources().isEmpty()) {
+ LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports());
+ }
+
+ final Map<SourceIdentifier, ParserRuleContext> asts =
+ Maps.transformEntries(Multimaps.transformValues(sources, EXTRACT_AST).asMap(), SQUASH_AST);
+
+ final ParseTreeWalker walker = new ParseTreeWalker();
+ final Map<SourceIdentifier, ModuleBuilder> sourceToBuilder = new LinkedHashMap<>();
+
+ for (Entry<SourceIdentifier, ParserRuleContext> entry : asts.entrySet()) {
+ final YangParserListenerImpl yangModelParser = new YangParserListenerImpl(entry.getKey().getName());
+ walker.walk(yangModelParser, entry.getValue());
+ ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
+
+ // FIXME: do we need to lug this around?
+ // moduleBuilder.setSource(source);
+ sourceToBuilder.put(entry.getKey(), moduleBuilder);
+ }
+ LOG.debug("Modules ready for integration");
+
+ final YangParserImpl parser = YangParserImpl.getInstance();
+ final Collection<Module> modules = parser.buildModules(sourceToBuilder.values());
+ LOG.debug("Integrated cross-references modules");
+
+ result = Optional.of(parser.assembleContext(modules));
+ } else {
+ result = Optional.absent();
+ }
+
+ synchronized (this) {
+ if (v == version) {
+ currentSchemaContext.set(result);
+ contextVersion = version;
+ return result;
+ }
+
+ LOG.debug("Context version {} expected {}, retry", version, v);
+ }
+ }
+ }
+}
import java.io.InputStream;
import java.util.concurrent.Callable;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformationException;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceTransformer;
+import org.opendaylight.yangtools.yang.parser.impl.YangModelBasicValidationListener;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* A {@link SchemaSourceTransformer} which handles translation of models from
* ASTSchemaSource representation.
*/
public final class TextToASTTransformer implements SchemaSourceTransformer<YangTextSchemaSource, ASTSchemaSource> {
+ private static final Logger LOG = LoggerFactory.getLogger(TextToASTTransformer.class);
private static final Function<Exception, SchemaSourceTransformationException> MAPPER = new ExceptionMapper<SchemaSourceTransformationException>("Source transformation", SchemaSourceTransformationException.class) {
@Override
protected SchemaSourceTransformationException newWithCause(final String message, final Throwable cause) {
return new SchemaSourceTransformationException(message, cause);
}
};
+
private final ListeningExecutorService executor;
- TextToASTTransformer(final ListeningExecutorService executor) {
+ private TextToASTTransformer(final ListeningExecutorService executor) {
this.executor = Preconditions.checkNotNull(executor);
}
+ public static final TextToASTTransformer create(final ListeningExecutorService executor) {
+ return new TextToASTTransformer(executor);
+ }
+
@Override
public Class<YangTextSchemaSource> getInputRepresentation() {
return YangTextSchemaSource.class;
public ASTSchemaSource call() throws IOException, YangSyntaxErrorException {
try (InputStream is = source.openStream()) {
final YangContext ctx = YangParserImpl.parseYangSource(is);
+ LOG.debug("Model {} parsed successfully", source);
+
+ final ParseTreeWalker walker = new ParseTreeWalker();
+ final YangModelBasicValidationListener validator = new YangModelBasicValidationListener();
+ walker.walk(validator, ctx);
+ LOG.debug("Model {} validated successfully", source);
+
return ASTSchemaSource.create(source.getIdentifier().getName(), ctx);
}
}