--- /dev/null
+/*
+ * Copyright (c) 2021 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.parser.stmt.reactor;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStatementState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Statement-reuse expansion of a single instance. The idea here is that a statement can end up being replicated the
+ * same way multiple times -- which does not typically happen, but when it does it is worth exploiting.
+ *
+ * @param <E> {@link EffectiveStatement} type
+ */
+final class EffectiveInstances<E extends EffectiveStatement<?, ?>> implements Mutable {
+ private static final Logger LOG = LoggerFactory.getLogger(EffectiveInstances.class);
+
+ // Note on sizing: this fits three entries without expansion. Note we do not include the local copy, as it does
+ // not have the same original.
+ private final Map<EffectiveStatementState, E> copies = new HashMap<>(4);
+ private final @NonNull E local;
+
+ EffectiveInstances(final E local) {
+ this.local = requireNonNull(local);
+ }
+
+ @SuppressWarnings("unchecked")
+ static <E extends EffectiveStatement<?, ?>> @NonNull E local(final Object obj) {
+ return obj instanceof EffectiveInstances ? ((EffectiveInstances<E>) obj).local : requireNonNull((E) obj);
+ }
+
+ @NonNull E attachCopy(final @NonNull EffectiveStatementState key, @NonNull final E copy) {
+ final E prev = copies.putIfAbsent(requireNonNull(key), requireNonNull(copy));
+ if (prev == null) {
+ // Nothing matching state
+ return copy;
+ }
+
+ // We need to make sure substatements are actually the same. If they are not, we'll just return the copy,
+ // playing it safe.
+ final Collection<? extends EffectiveStatement<?, ?>> prevStmts = prev.effectiveSubstatements();
+ final Collection<? extends EffectiveStatement<?, ?>> copyStmts = copy.effectiveSubstatements();
+ if (prevStmts != copyStmts) {
+ if (prevStmts.size() != copyStmts.size()) {
+ LOG.trace("Key {} substatement count mismatch", key);
+ return copy;
+ }
+
+ final Iterator<? extends EffectiveStatement<?, ?>> prevIt = prevStmts.iterator();
+ final Iterator<? extends EffectiveStatement<?, ?>> copyIt = copyStmts.iterator();
+ while (prevIt.hasNext()) {
+ if (prevIt.next() != copyIt.next()) {
+ LOG.trace("Key {} substatement mismatch", key);
+ return copy;
+ }
+ }
+ }
+
+ LOG.trace("Key {} substatement reused", key);
+ return prev;
+ }
+}
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
import org.opendaylight.yangtools.yang.parser.spi.SchemaTreeNamespace;
import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStatementState;
import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.OnDemandSchemaTreeStorageNode;
import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.StorageNodeType;
// First check if we can reuse the entire prototype
if (!factory.canReuseCurrent(this, prototype, origSubstatements)) {
- // FIXME: YANGTOOLS-1214: deduplicate this return
- return tryToReuseSubstatements(factory, origEffective);
+ return internAlongCopyAxis(factory, tryToReuseSubstatements(factory, origEffective));
}
// We can reuse this statement let's see if all statements agree...
prototype.decRef();
// Values are the effective copies, hence this efficiently deals with recursion.
- // FIXME: YANGTOOLS-1214: deduplicate this return
- return factory.createEffective(this, declared.stream(), effective.stream());
+ return internAlongCopyAxis(factory, factory.createEffective(this, declared.stream(), effective.stream()));
}
private @NonNull E tryToReuseSubstatements(final StatementFactory<A, D, E> factory, final @NonNull E original) {
return effective;
}
+ private @NonNull E internAlongCopyAxis(final StatementFactory<A, D, E> factory, final @NonNull E stmt) {
+ if (!isModified()) {
+ final EffectiveStatementState state = factory.extractEffectiveState(stmt);
+ if (state != null) {
+ return prototype.unmodifiedEffectiveSource().attachEffectiveCopy(state, stmt);
+ }
+ }
+ return stmt;
+ }
+
private List<ReactorStmtCtx<?, ?, ?>> reusePrototypeReplicas() {
return reusePrototypeReplicas(Streams.concat(prototype.streamDeclared(), prototype.streamEffective()));
}
import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStatementState;
import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
* {@link #buildEffective()} instance. If this context is reused, it can be inflated to {@link EffectiveInstances}
* and also act as a common instance reuse site.
*/
- private @Nullable E effectiveInstance;
+ private @Nullable Object effectiveInstance;
// Master flag controlling whether this context can yield an effective statement
// FIXME: investigate the mechanics that are being supported by this, as it would be beneficial if we can get rid
@Override
public final <X, Z extends EffectiveStatement<X, ?>> @NonNull Optional<X> findSubstatementArgument(
final @NonNull Class<Z> type) {
- final E existing = effectiveInstance;
+ final E existing = effectiveInstance();
return existing != null ? existing.findFirstEffectiveSubstatementArgument(type)
: findSubstatementArgumentImpl(type);
}
@Override
public final boolean hasSubstatement(final @NonNull Class<? extends EffectiveStatement<?, ?>> type) {
- final E existing = effectiveInstance;
+ final E existing = effectiveInstance();
return existing != null ? existing.findFirstEffectiveSubstatement(type).isPresent() : hasSubstatementImpl(type);
}
+ private E effectiveInstance() {
+ final Object existing = effectiveInstance;
+ return existing != null ? EffectiveInstances.local(existing) : null;
+ }
+
// Visible due to InferredStatementContext's override. At this point we do not have an effective instance available.
<X, Z extends EffectiveStatement<X, ?>> @NonNull Optional<X> findSubstatementArgumentImpl(
final @NonNull Class<Z> type) {
@Override
public final E buildEffective() {
- final E existing;
- return (existing = effectiveInstance) != null ? existing : loadEffective();
+ final Object existing;
+ return (existing = effectiveInstance) != null ? EffectiveInstances.local(existing) : loadEffective();
}
private @NonNull E loadEffective() {
abstract @NonNull E createEffective();
+ /**
+ * Attach an effective copy of this statement. This essentially acts as a map, where we make a few assumptions:
+ * <ul>
+ * <li>{@code copy} and {@code this} statement share {@link #getOriginalCtx()} if it exists</li>
+ * <li>{@code copy} did not modify any statements relative to {@code this}</li>
+ * </ul>
+ *
+ * @param state effective statement state, acting as a lookup key
+ * @param stmt New copy to append
+ * @return {@code stmt} or a previously-created instances with the same {@code state}
+ */
+ @SuppressWarnings("unchecked")
+ final @NonNull E attachEffectiveCopy(final @NonNull EffectiveStatementState state, final @NonNull E stmt) {
+ final Object local = effectiveInstance;
+ final EffectiveInstances<E> instances;
+ if (local instanceof EffectiveInstances) {
+ instances = (EffectiveInstances<E>) local;
+ } else {
+ effectiveInstance = instances = new EffectiveInstances<>((E) local);
+ }
+ return instances.attachCopy(state, stmt);
+ }
+
/**
* Walk this statement's copy history and return the statement closest to original which has not had its effective
* statements modified. This statement and returned substatement logically have the same set of substatements, hence
--- /dev/null
+/*
+ * Copyright (c) 2021 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.parser.spi.meta;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Significant state captured by an {@link EffectiveStatement} at its instantiation site. This can be used to compare
+ * statements which are instantiated through inference, for example through {@code uses} from {@code grouping}s, when
+ * the reactor establishes the two instances have the same substatements.
+ */
+@Beta
+public abstract class EffectiveStatementState implements Immutable {
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ @Override
+ public final String toString() {
+ return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+ }
+
+ protected abstract ToStringHelper addToStringAttributes(ToStringHelper helper);
+}
import java.util.Collection;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
*/
boolean canReuseCurrent(@NonNull Current<A, D> copy, @NonNull Current<A, D> current,
@NonNull Collection<? extends EffectiveStatement<?, ?>> substatements);
+
+ /**
+ * Return the {@link EffectiveStatementState} for a particular statement. This acts as a summary for comparison with
+ * statements created by this factory, without taking substatements into account. This is an optional operation, it
+ * is always safe to return null.
+ *
+ * @param stmt EffectiveStatement to examine
+ * @return EffectiveStatementState or null if the statement cannot be expressed
+ */
+ @Nullable EffectiveStatementState extractEffectiveState(@NonNull E stmt);
}
return policy.canReuseCurrent(copy, current, substatements);
}
+ @Override
+ public EffectiveStatementState extractEffectiveState(final E stmt) {
+ // Not implemented for most statements
+ return null;
+ }
+
/**
* Parses textual representation of argument in object representation.
*