import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
*/
final class InferredStatementContext<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
extends StatementContextBase<A, D, E> implements OnDemandSchemaTreeStorageNode {
+ // An effective copy view, with enough information to decide what to do next
+ private static final class EffectiveCopy implements Immutable {
+ // Original statement
+ private final ReactorStmtCtx<?, ?, ?> orig;
+ // Effective view, if the statement is to be reused it equals to orig
+ private final ReactorStmtCtx<?, ?, ?> copy;
+
+ EffectiveCopy(final ReactorStmtCtx<?, ?, ?> orig, final ReactorStmtCtx<?, ?, ?> copy) {
+ this.orig = requireNonNull(orig);
+ this.copy = requireNonNull(copy);
+ }
+
+ boolean isReused() {
+ return orig == copy;
+ }
+
+ ReactorStmtCtx<?, ?, ?> toChildContext(final @NonNull InferredStatementContext<?, ?, ?> parent) {
+ return isReused() ? orig.replicaAsChildOf(parent) : copy;
+ }
+
+ ReactorStmtCtx<?, ?, ?> toReusedChild(final @NonNull InferredStatementContext<?, ?, ?> parent) {
+ verify(isReused(), "Attempted to discard copy %s", copy);
+ return orig.replicaAsChildOf(parent);
+ }
+ }
+
private static final Logger LOG = LoggerFactory.getLogger(InferredStatementContext.class);
// Sentinel object for 'substatements'
}
// We can reuse this statement let's see if all the statements agree
- final List<Entry<Mutable<?, ?, ?>, Mutable<?, ?, ?>>> declared = prototype.streamDeclared()
+ final List<EffectiveCopy> declCopy = prototype.streamDeclared()
.filter(StmtContext::isSupportedByFeatures)
.map(sub -> effectiveCopy((ReactorStmtCtx<?, ?, ?>) sub))
.filter(Objects::nonNull)
.collect(Collectors.toUnmodifiableList());
- final List<Entry<Mutable<?, ?, ?>, Mutable<?, ?, ?>>> effective = prototype.streamEffective()
+ final List<EffectiveCopy> effCopy = prototype.streamEffective()
.map(sub -> effectiveCopy((ReactorStmtCtx<?, ?, ?>) sub))
.filter(Objects::nonNull)
.collect(Collectors.toUnmodifiableList());
- // We no longer need the prototype's substatements, but we may need to retain ours
- prototype.decRef();
- if (haveRef()) {
- substatements = Streams.concat(declared.stream(), effective.stream())
- .map(Entry::getValue)
- .collect(ImmutableList.toImmutableList());
- } else {
- // This should immediately get swept anyway. Should we use a poison object?
- substatements = List.of();
- }
-
- if (allReused(declared) && allReused(effective)) {
+ if (allReused(declCopy) && allReused(effCopy)) {
LOG.debug("Reusing after substatement check: {}", origEffective);
+ // FIXME: can we skip this if !haveRef()?
+ substatements = reusePrototypeReplicas(Streams.concat(declCopy.stream(), effCopy.stream())
+ .map(copy -> copy.toReusedChild(this)));
+ prototype.decRef();
return origEffective;
}
- // Values are the effective copies, hence this efficienly deals with recursion.
- return factory.createEffective(this, declared.stream().map(Entry::getValue),
- effective.stream().map(Entry::getValue));
+ final List<ReactorStmtCtx<?, ?, ?>> declared = declCopy.stream()
+ .map(copy -> copy.toChildContext(this))
+ .collect(ImmutableList.toImmutableList());
+ final List<ReactorStmtCtx<?, ?, ?>> effective = effCopy.stream()
+ .map(copy -> copy.toChildContext(this))
+ .collect(ImmutableList.toImmutableList());
+ substatements = declared.isEmpty() ? effective
+ : Streams.concat(declared.stream(), effective.stream()).collect(ImmutableList.toImmutableList());
+ prototype.decRef();
+
+ // Values are the effective copies, hence this efficiently deals with recursion.
+ return factory.createEffective(this, declared.stream(), effective.stream());
}
private @NonNull E tryToReuseSubstatements(final StatementFactory<A, D, E> factory,
}
private List<ReactorStmtCtx<?, ?, ?>> reusePrototypeReplicas() {
- return Streams.concat(
+ return reusePrototypeReplicas(Streams.concat(
prototype.streamDeclared().filter(StmtContext::isSupportedByFeatures),
- prototype.streamEffective())
+ prototype.streamEffective()));
+ }
+
+ private List<ReactorStmtCtx<?, ?, ?>> reusePrototypeReplicas(final Stream<StmtContext<?, ?, ?>> stream) {
+ return stream
.map(stmt -> {
final ReplicaStatementContext<?, ?, ?> ret = ((ReactorStmtCtx<?, ?, ?>) stmt).replicaAsChildOf(this);
ret.buildEffective();
return true;
}
- private static boolean allReused(final List<Entry<Mutable<?, ?, ?>, Mutable<?, ?, ?>>> entries) {
- for (Entry<Mutable<?, ?, ?>, Mutable<?, ?, ?>> entry : entries) {
- if (entry.getKey() != entry.getValue()) {
- return false;
- }
- }
- return true;
+ private static boolean allReused(final List<EffectiveCopy> entries) {
+ return entries.stream().allMatch(EffectiveCopy::isReused);
}
@Override
YangStmtMapping.TYPEDEF,
YangStmtMapping.USES);
- private Map.Entry<Mutable<?, ?, ?>, Mutable<?, ?, ?>> effectiveCopy(final ReactorStmtCtx<?, ?, ?> stmt) {
+ private EffectiveCopy effectiveCopy(final ReactorStmtCtx<?, ?, ?> stmt) {
// FIXME: YANGTOOLS-652: formerly known as "isReusedByUses"
if (REUSED_DEF_SET.contains(stmt.definition().getPublicView())) {
- return Map.entry(stmt, stmt.replicaAsChildOf(this));
+ return new EffectiveCopy(stmt, stmt);
}
final ReactorStmtCtx<?, ?, ?> effective = stmt.asEffectiveChildOf(this, childCopyType, targetModule);
- return effective == null ? null : Map.entry(stmt, effective);
+ return effective == null ? null : new EffectiveCopy(stmt, effective);
}
private void copySubstatement(final Mutable<?, ?, ?> substatement, final Collection<Mutable<?, ?, ?>> buffer,
*/
package org.opendaylight.yangtools.yang.stmt;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import java.net.URI;
+import java.util.List;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.GroupingEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
public class YT1212Test {
@Test
// The 'type' is not context-independent, but it being copy-insensitive and statements get reused
assertSame(foo.effectiveSubstatements(), grpFoo.effectiveSubstatements());
}
+
+ @Test
+ public void testContainerStatementReuse() throws Exception {
+ final ModuleEffectiveStatement module = StmtTestUtils.parseYangSource("/bugs/YT1212/container.yang")
+ .getModuleStatements()
+ .get(QNameModule.create(URI.create("foo")));
+ assertNotNull(module);
+
+ final NotificationEffectiveStatement notif =
+ module.findFirstEffectiveSubstatement(NotificationEffectiveStatement.class).orElseThrow();
+ final List<GroupingEffectiveStatement> groupings = notif.effectiveSubstatements().stream()
+ .filter(GroupingEffectiveStatement.class::isInstance).map(GroupingEffectiveStatement.class::cast)
+ .collect(Collectors.toList());
+ assertEquals(2, groupings.size());
+ final GroupingEffectiveStatement grp = groupings.get(0);
+ assertEquals("grp", grp.argument().getLocalName());
+ final GroupingEffectiveStatement barGrp = groupings.get(1);
+ assertEquals("bar", barGrp.argument().getLocalName());
+ final ContainerEffectiveStatement bar = notif.findFirstEffectiveSubstatement(ContainerEffectiveStatement.class)
+ .orElseThrow();
+
+ // Container needs to be reused
+ assertSame(bar.findFirstEffectiveSubstatement(ContainerEffectiveStatement.class).orElseThrow(),
+ barGrp.findFirstEffectiveSubstatement(ContainerEffectiveStatement.class).orElseThrow());
+ }
}