--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech s.r.o. 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.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+/**
+ * A set of features.
+ *
+ * <p>
+ * The semantics of {@link #contains(QName)} is a bit funky, depending on implementation:
+ * <ol>
+ * <li>explicit implementation, returned from {@link #of()}, {@link #of(Set)} et al., delegates to the underlying
+ * Set's {@link Set#contains(Object)}, while on the other hand</li>
+ * <li>sparse implementation, constructed via {@link #builder()} and {@link Builder#build()}, carves the features into
+ * well-known {@code module} namespaces, expressed as {@link QNameModule} for which we have an explicit
+ * enumeration of supported features. All other {@code module} namespaces are treated as if there was no
+ * specification of supported features -- e.g. all features from those namespaces are deemed to be present
+ * in the instance.</li>
+ * </ol>
+ */
+public abstract sealed class FeatureSet implements Immutable {
+ private static final class Explicit extends FeatureSet {
+ private static final @NonNull Explicit EMPTY = new Explicit(ImmutableSet.of());
+
+ private final ImmutableSet<QName> features;
+
+ Explicit(final ImmutableSet<QName> features) {
+ this.features = requireNonNull(features);
+ }
+
+ @Override
+ public boolean contains(final QName qname) {
+ return features.contains(qname);
+ }
+
+ @Override
+ public int hashCode() {
+ return features.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return this == obj || obj instanceof Explicit other && features.equals(other.features);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("features", features).toString();
+ }
+ }
+
+ private static final class Sparse extends FeatureSet {
+ // Note: not a ImmutableSetMultimap because we need to distinguish non-presence vs. empty Set
+ private final ImmutableMap<QNameModule, ImmutableSet<String>> featuresByModule;
+
+ Sparse(final Map<QNameModule, ImmutableSet<String>> featuresByModule) {
+ this.featuresByModule = ImmutableMap.copyOf(featuresByModule);
+ }
+
+ @Override
+ public boolean contains(final QName qname) {
+ final var sets = featuresByModule.get(qname.getModule());
+ return sets == null || sets.contains(qname.getLocalName());
+ }
+
+ @Override
+ public int hashCode() {
+ return featuresByModule.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return this == obj || obj instanceof Sparse other && featuresByModule.equals(other.featuresByModule);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("features", featuresByModule).toString();
+ }
+ }
+
+ /**
+ * A builder for a sparse FeatureSet. The semantics is such that for features which belong to a namespace which
+ * has been explicitly mention, only the specified features are supported. For namespaces not mentioned, all
+ * features are reported as present.
+ */
+ public static final class Builder {
+ // Note: Tree{Map,Set} so we sort keys/entries by their natural ordering -- which also prevents nulls
+ private final TreeMap<QNameModule, TreeSet<String>> moduleFeatures = new TreeMap<>();
+
+ public @NonNull Builder addModuleFeatures(final QNameModule module, final Collection<String> names) {
+ moduleFeatures.computeIfAbsent(module, ignored -> new TreeSet<>()).addAll(names);
+ return this;
+ }
+
+ public @NonNull FeatureSet build() {
+ return new Sparse(Maps.transformValues(moduleFeatures, ImmutableSet::copyOf));
+ }
+ }
+
+ /**
+ * Return an empty {@link FeatureSet}.
+ *
+ * @return An empty {@link FeatureSet}
+ */
+ public static @NonNull FeatureSet of() {
+ return Explicit.EMPTY;
+ }
+
+ /**
+ * Return a {@link FeatureSet} containing specified features.
+ *
+ * @return A {@link FeatureSet}
+ * @throws NullPointerException if {@code features} is or contains {@code null}
+ */
+ public static @NonNull FeatureSet of(final QName... features) {
+ return of(ImmutableSet.copyOf(features));
+ }
+
+ /**
+ * Return a {@link FeatureSet} containing specified features.
+ *
+ * @return A {@link FeatureSet}
+ * @throws NullPointerException if {@code features} is or contains {@code null}
+ */
+ public static @NonNull FeatureSet of(final Set<QName> features) {
+ return of(ImmutableSet.copyOf(features));
+ }
+
+ private static @NonNull FeatureSet of(final ImmutableSet<QName> features) {
+ return features.isEmpty() ? of() : new Explicit(features);
+ }
+
+ public static @NonNull Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Determine whether a particular {@code feature}, as identified by its {@link QName} is part of this set.
+ *
+ * @param qname Feature QName
+ * @return {@code true} if this set contains the feature
+ * @throws NullPointerException if {@code qname} is {@code null}
+ */
+ public abstract boolean contains(@NonNull QName qname);
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @Override
+ public abstract String toString();
+}
* <p>
* The set of features referenced in this expression is available through {@link #getReferencedFeatures()}.
*/
-public abstract sealed class IfFeatureExpr implements Immutable, Predicate<Set<QName>> {
+public abstract sealed class IfFeatureExpr implements Immutable, Predicate<FeatureSet> {
private abstract static sealed class Single extends IfFeatureExpr {
final @NonNull QName qname;
}
@Override
- public boolean test(final Set<QName> supportedFeatures) {
+ public boolean test(final FeatureSet supportedFeatures) {
return !supportedFeatures.contains(qname);
}
}
@Override
- public boolean test(final Set<QName> supportedFeatures) {
+ public boolean test(final FeatureSet supportedFeatures) {
return supportedFeatures.contains(qname);
}
}
@Override
- public boolean test(final Set<QName> supportedFeatures) {
+ public boolean test(final FeatureSet supportedFeatures) {
for (var expr : array) {
if (!expr.test(supportedFeatures)) {
return false;
}
@Override
- public boolean test(final Set<QName> supportedFeatures) {
+ public boolean test(final FeatureSet supportedFeatures) {
for (var expr : array) {
if (expr.test(supportedFeatures)) {
return true;
}
@Override
- public final boolean test(final Set<QName> supportedFeatures) {
+ public final boolean test(final FeatureSet supportedFeatures) {
final boolean neg = negated();
for (var qname : array) {
if (supportedFeatures.contains(qname) == neg) {
}
@Override
- public final boolean test(final Set<QName> supportedFeatures) {
+ public final boolean test(final FeatureSet supportedFeatures) {
for (var qname : array) {
if (supportedFeatures.contains(qname)) {
return !negated();
}
/**
- * Construct an assertion that a feature is present in the set passed to {@link #test(Set)}.
+ * Construct an assertion that a feature is present in the set passed to {@link #test(FeatureSet)}.
*
* @param qname Feature QName
* @return An expression
public abstract @NonNull IfFeatureExpr negate();
@Override
- public abstract boolean test(Set<QName> supportedFeatures);
+ public abstract boolean test(FeatureSet supportedFeatures);
@Override
public abstract int hashCode();
--- /dev/null
+/*
+ * Copyright (c) 2023 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.stmt;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.junit.jupiter.api.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+
+class FeatureSetTest {
+ private static final @NonNull QName FOO_FOO = QName.create("foo", "foo");
+ private static final @NonNull QName FOO_BAR = QName.create("foo", "bar");
+ private static final @NonNull QName BAR_FOO = QName.create("bar", "foo");
+ private static final @NonNull QName BAR_BAR = QName.create("bar", "bar");
+
+ @Test
+ void emptyIsSingleton() {
+ assertSame(FeatureSet.of(), FeatureSet.of());
+ }
+
+ @Test
+ void explicitContains() {
+ final var set = FeatureSet.of(FOO_FOO, BAR_FOO);
+ assertTrue(set.contains(FOO_FOO));
+ assertFalse(set.contains(FOO_BAR));
+ assertTrue(set.contains(BAR_FOO));
+ assertFalse(set.contains(BAR_BAR));
+ }
+
+ @Test
+ void explicitHashCodeEquals() {
+ final var set = FeatureSet.of(FOO_FOO, BAR_FOO);
+ final var other = FeatureSet.of(Set.of(FOO_FOO, BAR_FOO));
+ assertEquals(set.hashCode(), other.hashCode());
+ assertEquals(set, other);
+ }
+
+ @Test
+ void sparseContains() {
+ final var set = FeatureSet.builder()
+ .addModuleFeatures(FOO_FOO.getModule(), Set.of(FOO_FOO.getLocalName()))
+ .build();
+
+ assertTrue(set.contains(FOO_FOO));
+ assertFalse(set.contains(FOO_BAR));
+ assertTrue(set.contains(BAR_FOO));
+ assertTrue(set.contains(BAR_BAR));
+ }
+
+ @Test
+ void sparseHashCodeEquals() {
+ final var set = FeatureSet.builder()
+ .addModuleFeatures(FOO_FOO.getModule(), Set.of(FOO_FOO.getLocalName()))
+ .build();
+ final var other = FeatureSet.builder()
+ .addModuleFeatures(FOO_FOO.getModule(), Set.of(FOO_FOO.getLocalName()))
+ .build();
+ assertEquals(set.hashCode(), other.hashCode());
+ assertEquals(set, other);
+ }
+}
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
-import com.google.common.collect.ImmutableSet;
import java.util.Optional;
import org.junit.Test;
import org.opendaylight.yangtools.rfc8040.model.api.YangDataSchemaNode;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.XMLNamespace;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException;
import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
@Test
public void testIfFeatureStatementBeingIgnoredInYangDataBody() throws Exception {
final var schemaContext = REACTOR.newBuild()
- .setSupportedFeatures(ImmutableSet.of())
+ .setSupportedFeatures(FeatureSet.of())
.addSources(FOOBAR_MODULE, IETF_RESTCONF_MODULE)
.buildEffective();
assertNotNull(schemaContext);
import java.io.IOException;
import java.util.Collection;
import java.util.List;
-import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
/**
* @param supportedFeatures Set of supported features in the final SchemaContext. If the set is empty, no features
* encountered will be supported.
*/
- @NonNull YangParser setSupportedFeatures(@NonNull Set<QName> supportedFeatures);
+ @NonNull YangParser setSupportedFeatures(@NonNull FeatureSet supportedFeatures);
/**
* Set YANG modules which can be deviated by specified modules during the parsing process. Map key (QNameModule)
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
import org.opendaylight.yangtools.yang.parser.api.YangLibModuleSet;
import org.opendaylight.yangtools.yang.parser.api.YangLibResolver;
}
try {
- return act.setSupportedFeatures(features.build()).buildEffective();
+ return act.setSupportedFeatures(FeatureSet.of(features.build())).buildEffective();
} catch (ReactorException e) {
throw DefaultYangParser.decodeReactorException(e);
}
import java.io.IOException;
import java.util.Collection;
import java.util.List;
-import java.util.Set;
import javax.xml.transform.TransformerException;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
import org.opendaylight.yangtools.yang.model.repo.api.YangIRSchemaSource;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
}
@Override
- public @NonNull YangParser setSupportedFeatures(final Set<QName> supportedFeatures) {
+ public @NonNull YangParser setSupportedFeatures(final FeatureSet supportedFeatures) {
buildAction.setSupportedFeatures(supportedFeatures);
return this;
}
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 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.api.stmt.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 synchronized @Nullable FeatureSet getSupportedFeatures() {
var local = supportedFeatures;
if (local == null && !registeredFeatures.isEmpty()) {
- final var builder = ImmutableMap.<QNameModule, ImmutableSet<String>>builder();
+ final var builder = FeatureSet.builder();
for (var entry : registeredFeatures.entrySet()) {
- builder.put(entry.getKey(), entry.getValue().stream()
- .flatMap(Set::stream)
- .distinct()
- .sorted()
- .collect(ImmutableSet.toImmutableSet()));
+ for (var features : entry.getValue()) {
+ builder.addModuleFeatures(entry.getKey(), features);
+ }
}
- supportedFeatures = local = new FeatureSet(builder.build());
+ supportedFeatures = local = builder.build();
}
return local;
}
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListenableFuture;
-import java.util.Set;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
import org.opendaylight.yangtools.yang.model.repo.api.YangIRSchemaSource;
@Test
public void testSharedSchemaRepositoryWithSomeFeaturesSupported() throws Exception {
- final Set<QName> supportedFeatures = ImmutableSet.of(QName.create("foobar-namespace", "test-feature-1"));
+ final FeatureSet supportedFeatures = FeatureSet.of(QName.create("foobar-namespace", "test-feature-1"));
final SharedSchemaRepository sharedSchemaRepository = new SharedSchemaRepository(
"shared-schema-repo-with-features-test");
final ListenableFuture<EffectiveModelContext> testSchemaContextFuture =
sharedSchemaRepository.createEffectiveModelContextFactory(
- SchemaContextFactoryConfiguration.builder().setSupportedFeatures(ImmutableSet.of()).build())
+ SchemaContextFactoryConfiguration.builder().setSupportedFeatures(FeatureSet.of()).build())
.createEffectiveModelContext(foobar.getId());
assertTrue(testSchemaContextFuture.isDone());
assertSchemaContext(testSchemaContextFuture.get(), 1);
import org.opendaylight.yangtools.yang.common.YangVersion;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
-import org.opendaylight.yangtools.yang.model.repo.api.FeatureSet;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
import org.opendaylight.yangtools.yang.parser.spi.ParserNamespaces;
import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
libSources.add(new SourceSpecificContext(this, libSource));
}
- void setSupportedFeatures(final Set<QName> supportedFeatures) {
- if (supportedFeatures instanceof FeatureSet) {
- addToNamespace(ParserNamespaces.SUPPORTED_FEATURES, Empty.value(), supportedFeatures);
- } else {
- addToNamespace(ParserNamespaces.SUPPORTED_FEATURES, Empty.value(), ImmutableSet.copyOf(supportedFeatures));
- }
+ void setSupportedFeatures(final FeatureSet supportedFeatures) {
+ addToNamespace(ParserNamespaces.SUPPORTED_FEATURES, Empty.value(), requireNonNull(supportedFeatures));
}
void setModulesDeviatedByModules(final SetMultimap<QNameModule, QNameModule> modulesDeviatedByModules) {
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
-import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.concepts.Mutable;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
import org.opendaylight.yangtools.yang.parser.spi.meta.StatementSupportBundle;
* If the set is empty, no features encountered will be supported.
* @return This build action, for fluent use.
*/
- public @NonNull BuildAction setSupportedFeatures(final @NonNull Set<QName> supportedFeatures) {
+ public @NonNull BuildAction setSupportedFeatures(final @NonNull FeatureSet supportedFeatures) {
checkState(!supportedFeaturesSet, "Supported features should be set only once.");
context.setSupportedFeatures(requireNonNull(supportedFeatures));
supportedFeaturesSet = true;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
*/
if (isParentSupportedByFeatures()) {
// If the set of supported features has not been provided, all features are supported by default.
- final Set<QName> supportedFeatures = namespaceItem(ParserNamespaces.SUPPORTED_FEATURES, Empty.value());
+ final var supportedFeatures = namespaceItem(ParserNamespaces.SUPPORTED_FEATURES, Empty.value());
if (supportedFeatures == null || StmtContextUtils.checkFeatureSupport(this, supportedFeatures)) {
flags |= SET_SUPPORTED_BY_FEATURES;
return true;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
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.api.YinTextSchemaSource;
throws ReactorException {
final BuildAction build = getReactor(config).newBuild().addSources(sources);
if (supportedFeatures != null) {
- build.setSupportedFeatures(supportedFeatures);
+ build.setSupportedFeatures(FeatureSet.of(supportedFeatures));
}
return build.buildEffective();
}
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.model.repo.api.YinTextSchemaSource;
import org.opendaylight.yangtools.yang.parser.rfc7950.reactor.RFC7950Reactors;
final var action = RFC7950Reactors.defaultReactor().newBuild()
.addSources(loadSources(cls, resourceDirectory));
if (supportedFeatures != null) {
- action.setSupportedFeatures(supportedFeatures);
+ action.setSupportedFeatures(FeatureSet.of(supportedFeatures));
}
return action.buildEffective();
}
TestUtils.class.getResource(resourcePath).toURI()))));
}
if (supportedFeatures != null) {
- reactor.setSupportedFeatures(supportedFeatures);
+ reactor.setSupportedFeatures(FeatureSet.of(supportedFeatures));
}
return reactor.buildEffective();
}
import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.Optional;
-import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ExtensionStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.FeatureEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.api.stmt.FeatureStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.GroupingEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.GroupingStatement;
public static final @NonNull ParserNamespace<StmtContext<?, ?, ?>, QNameModule> MODULECTX_TO_QNAME =
new ParserNamespace<>("modulectx-to-qnamemodule");
- public static final @NonNull ParserNamespace<Empty, Set<QName>> SUPPORTED_FEATURES =
+ public static final @NonNull ParserNamespace<Empty, FeatureSet> SUPPORTED_FEATURES =
new ParserNamespace<>("supportedFeatures");
/**
import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.api.stmt.IfFeatureExpr;
import org.opendaylight.yangtools.yang.model.api.stmt.KeyEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.KeyStatement;
}
public static boolean checkFeatureSupport(final StmtContext<?, ?, ?> stmtContext,
- final Set<QName> supportedFeatures) {
+ final FeatureSet supportedFeatures) {
boolean isSupported = false;
boolean containsIfFeature = false;
for (var stmt : stmtContext.declaredSubstatements()) {
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.YangConstants;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.parser.api.YangParser;
final Set<QName> supportedFeatures, final Collection<? extends SchemaSourceRepresentation> sources) {
final YangParser parser = PARSER_FACTORY.createParser(config);
if (supportedFeatures != null) {
- parser.setSupportedFeatures(supportedFeatures);
+ parser.setSupportedFeatures(FeatureSet.of(supportedFeatures));
}
try {
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.YangConstants;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
import org.opendaylight.yangtools.yang.parser.api.YangParser;
import org.opendaylight.yangtools.yang.parser.api.YangParserConfiguration;
.warnForUnkeyedLists(warnForUnkeyedLists).build();
final YangParser parser = PARSER_FACTORY.createParser(configuration);
if (supportedFeatures != null) {
- parser.setSupportedFeatures(supportedFeatures);
+ parser.setSupportedFeatures(FeatureSet.of(supportedFeatures));
}
for (File file : testFiles) {
+++ /dev/null
-/*
- * Copyright (c) 2023 PANTHEON.tech s.r.o. 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.repo.api;
-
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.annotations.Beta;
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import java.util.AbstractSet;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Set;
-import org.opendaylight.yangtools.concepts.Immutable;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
-
-/**
- * Set of features. This is nominally a {@link Set} due to API pre-existing API contracts. This class needs to be used
- * <b>very carefully</b> because its {@link #hashCode()} and {@link #equals(Object)} contracts do not conform to
- * the specification laid out by {@link Set} and it cannot enumerate its individual component {@link QName}s -- thus
- * breaking reflexivity requirement of {@link #equals(Object)}.
- *
- * <p>
- * The semantics of {@link #contains(Object)} is a bit funky, but reflects the default of supporting all encountered
- * features without enumerating them. The map supplied to the constructor enumerates all {@code module} namespaces,
- * expressed as {@link QNameModule} for which we have an explicit enumeration of supported features. All other
- * {@code module} namespaces are treated as if there was no specification of supported features -- e.g. all features
- * from those namespaces are deemed to be present in the instance.
- */
-// FIXME: 12.0.0: this should only have 'boolean contains(QName)', with two implementations (Set-based and
-// module/feature based via a builder). This shouldlive in yang-model-api, where it has a tie-in
-// with IfFeatureExpr
-@Beta
-public final class FeatureSet extends AbstractSet<QName> implements Immutable {
- // Note: not a ImmutableSetMultimap because we need to distinguish non-presence vs. empty Set
- private final ImmutableMap<QNameModule, ImmutableSet<String>> featuresByModule;
-
- public FeatureSet(final ImmutableMap<QNameModule, ImmutableSet<String>> featuresByModule) {
- this.featuresByModule = requireNonNull(featuresByModule);
- }
-
- @Override
- @SuppressWarnings("checkstyle:parameterName")
- public boolean contains(final Object o) {
- if (o instanceof QName qname) {
- final var features = featuresByModule.get(qname.getModule());
- return features == null || features.contains(qname.getLocalName());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return featuresByModule.hashCode();
- }
-
- @Override
- public boolean equals(final Object obj) {
- return this == obj || obj instanceof FeatureSet other && featuresByModule.equals(other.featuresByModule);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this).add("features", featuresByModule).toString();
- }
-
- @Deprecated
- @Override
- public Iterator<QName> iterator() {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- public boolean isEmpty() {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- public int size() {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- public Object[] toArray() {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- @SuppressWarnings("checkstyle:parameterName")
- public <T> T[] toArray(final T[] a) {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- @SuppressWarnings("checkstyle:parameterName")
- public boolean add(final QName e) {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- @SuppressWarnings("checkstyle:parameterName")
- public boolean remove(final Object o) {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- @SuppressWarnings("checkstyle:parameterName")
- public boolean addAll(final Collection<? extends QName> c) {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- @SuppressWarnings("checkstyle:parameterName")
- public boolean retainAll(final Collection<?> c) {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- @SuppressWarnings("checkstyle:parameterName")
- public boolean removeAll(final Collection<?> c) {
- throw new UnsupportedOperationException();
- }
-
- @Deprecated
- @Override
- public void clear() {
- throw new UnsupportedOperationException();
- }
-}
import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.concepts.Mutable;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
/**
* SchemaContextFactory configuration class. It currently supports the following options to be set:
private final @NonNull SchemaSourceFilter filter;
private final @NonNull StatementParserMode statementParserMode;
- private final @Nullable Set<QName> supportedFeatures;
+ private final @Nullable FeatureSet supportedFeatures;
private final @Nullable ImmutableSetMultimap<QNameModule, QNameModule> modulesDeviatedByModules;
private SchemaContextFactoryConfiguration(final @NonNull SchemaSourceFilter filter,
final @NonNull StatementParserMode statementParserMode,
- final @Nullable Set<QName> supportedFeatures,
+ final @Nullable FeatureSet supportedFeatures,
final @Nullable ImmutableSetMultimap<QNameModule, QNameModule> modulesDeviatedByModules) {
this.filter = requireNonNull(filter);
this.statementParserMode = requireNonNull(statementParserMode);
return statementParserMode;
}
- public Optional<Set<QName>> getSupportedFeatures() {
+ public Optional<FeatureSet> getSupportedFeatures() {
return Optional.ofNullable(supportedFeatures);
}
public boolean equals(final Object obj) {
return this == obj || obj instanceof SchemaContextFactoryConfiguration other && filter.equals(other.filter)
&& statementParserMode.equals(other.statementParserMode)
- && equals(supportedFeatures, other.supportedFeatures)
+ && Objects.equals(supportedFeatures, other.supportedFeatures)
&& Objects.equals(modulesDeviatedByModules, other.modulesDeviatedByModules);
}
- // This a bit of a dance to deal with FeatureSet not conforming to Set.equals()
- private static boolean equals(final @Nullable Set<QName> thisFeatures, final @Nullable Set<QName> otherFeatures) {
- if (thisFeatures == otherFeatures) {
- return true;
- }
- if (thisFeatures == null || otherFeatures == null) {
- return false;
- }
- if (thisFeatures instanceof FeatureSet) {
- return thisFeatures.equals(otherFeatures);
- }
- if (otherFeatures instanceof FeatureSet) {
- return otherFeatures.equals(thisFeatures);
- }
- return thisFeatures.equals(otherFeatures);
- }
-
@Override
public String toString() {
return MoreObjects.toStringHelper(this).omitNullValues().add("schemaSourceFilter", filter)
private @NonNull SchemaSourceFilter filter = SchemaSourceFilter.ALWAYS_ACCEPT;
private @NonNull StatementParserMode statementParserMode = StatementParserMode.DEFAULT_MODE;
private ImmutableSetMultimap<QNameModule, QNameModule> modulesDeviatedByModules;
- private Set<QName> supportedFeatures;
+ private FeatureSet supportedFeatures;
/**
* Set schema source filter which will filter available schema sources using the provided filter.
* features encountered will be supported.
* @return this builder
*/
- public @NonNull Builder setSupportedFeatures(final Set<QName> supportedFeatures) {
- if (supportedFeatures == null || supportedFeatures instanceof FeatureSet) {
- this.supportedFeatures = supportedFeatures;
- } else {
- this.supportedFeatures = ImmutableSet.copyOf(supportedFeatures);
- }
+ public @NonNull Builder setSupportedFeatures(final FeatureSet supportedFeatures) {
+ this.supportedFeatures = supportedFeatures;
return this;
}