import com.google.common.annotations.Beta;
import com.google.common.base.Verify;
import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.FluentFuture;
import java.io.IOException;
import java.net.URL;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
+import org.checkerframework.checker.lock.qual.GuardedBy;
import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.AbstractRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.util.concurrent.FluentFutures;
+import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.repo.api.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
private final Collection<SourceIdentifier> requiredSources = new ConcurrentLinkedDeque<>();
private final Multimap<SourceIdentifier, YangTextSchemaSource> texts = ArrayListMultimap.create();
+ @GuardedBy("this")
+ private final Map<QNameModule, List<ImmutableSet<String>>> registeredFeatures = new HashMap<>();
private final AtomicReference<Optional<EffectiveModelContext>> currentSchemaContext =
new AtomicReference<>(Optional.empty());
private final GuavaSchemaSourceCache<YangIRSchemaSource> cache;
private final SchemaListenerRegistration transReg;
private final SchemaSourceRegistry registry;
private final SchemaRepository repository;
+
private volatile Object version = new Object();
private volatile Object contextVersion = version;
+ @GuardedBy("this")
+ private FeatureSet supportedFeatures = null;
private YangTextSchemaContextResolver(final SchemaRepository repository, final SchemaSourceRegistry registry) {
this.repository = requireNonNull(repository);
return registerSource(YangTextSchemaSource.forURL(url, guessSourceIdentifier(fileName)));
}
+ /**
+ * Register a {@link QNameModule} as a known module namespace with a set of supported features. Union of these
+ * registrations is forwarded to {@link FeatureSet} and this is then used in {@link #getEffectiveModelContext()} and
+ * related methods.
+ *
+ * @param module Module namespace
+ * @param features Features supported for that module namespace
+ * @return a {@link Registration}, use {@link Registration#close()} to revert the effects of this method
+ * @throws NullPointerException if any argument is {@code null}
+ */
+ public @NonNull Registration registerSupportedFeatures(final QNameModule module, final Set<String> features) {
+ final var checked = requireNonNull(module);
+ final var copy = ImmutableSet.copyOf(features);
+
+ synchronized (this) {
+ version = new Object();
+ supportedFeatures = null;
+ registeredFeatures.computeIfAbsent(module, ignored -> new ArrayList<>()).add(copy);
+ }
+ return new AbstractRegistration() {
+ @Override
+ protected void removeRegistration() {
+ removeFeatures(checked, copy);
+ }
+ };
+ }
+
+ private synchronized void removeFeatures(final QNameModule module, final ImmutableSet<String> features) {
+ final var moduleFeatures = registeredFeatures.get(module);
+ if (moduleFeatures != null && moduleFeatures.remove(features)) {
+ if (moduleFeatures.isEmpty()) {
+ registeredFeatures.remove(module);
+ }
+ supportedFeatures = null;
+ version = new Object();
+ }
+ }
+
+ private synchronized @Nullable FeatureSet getSupportedFeatures() {
+ var local = supportedFeatures;
+ if (local == null && !registeredFeatures.isEmpty()) {
+ final var builder = ImmutableMap.<QNameModule, ImmutableSet<String>>builder();
+ for (var entry : registeredFeatures.entrySet()) {
+ builder.put(entry.getKey(), entry.getValue().stream()
+ .flatMap(Set::stream)
+ .distinct()
+ .sorted()
+ .collect(ImmutableSet.toImmutableSet()));
+ }
+ supportedFeatures = local = new FeatureSet(builder.build());
+ }
+ return local;
+ }
+
private static SourceIdentifier guessSourceIdentifier(final @NonNull String fileName) {
try {
return YangTextSchemaSource.identifierFromFilename(fileName);
*/
public Optional<? extends EffectiveModelContext> getEffectiveModelContext(
final StatementParserMode statementParserMode) {
- final var factory = repository.createEffectiveModelContextFactory(config(statementParserMode));
Optional<EffectiveModelContext> sc;
Object ver;
do {
sources = ImmutableSet.copyOf(requiredSources);
} while (ver != version);
+ final var factory = repository.createEffectiveModelContextFactory(
+ config(statementParserMode, getSupportedFeatures()));
+
while (true) {
final var f = factory.createEffectiveModelContext(sources);
try {
@SuppressWarnings("checkstyle:avoidHidingCauseException")
public EffectiveModelContext trySchemaContext(final StatementParserMode statementParserMode)
throws SchemaResolutionException {
- final var future = repository.createEffectiveModelContextFactory(config(statementParserMode))
+ final var future = repository
+ .createEffectiveModelContextFactory(config(statementParserMode, getSupportedFeatures()))
.createEffectiveModelContext(ImmutableSet.copyOf(requiredSources));
try {
transReg.close();
}
- private static @NonNull SchemaContextFactoryConfiguration config(final StatementParserMode statementParserMode) {
- return SchemaContextFactoryConfiguration.builder().setStatementParserMode(statementParserMode).build();
+ private static @NonNull SchemaContextFactoryConfiguration config(
+ final StatementParserMode statementParserMode, final @Nullable FeatureSet supportedFeatures) {
+ final var builder = SchemaContextFactoryConfiguration.builder().setStatementParserMode(statementParserMode);
+ if (supportedFeatures != null) {
+ builder.setSupportedFeatures(supportedFeatures);
+ }
+ return builder.build();
}
}