import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.generator.impl.reactor.OriginalLink.Partial;
import org.opendaylight.mdsal.binding.model.api.Enumeration;
import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
import org.opendaylight.mdsal.binding.model.api.GeneratedType;
private List<AbstractAugmentGenerator> augments = List.of();
private List<GroupingGenerator> groupings;
+ // Performance optimization: if this is true, we have ascertained our original definition as well that of all our
+ // children
+ private boolean originalsResolved;
+
AbstractCompositeGenerator(final T statement) {
super(statement);
children = createChildren(statement);
augments.add(requireNonNull(augment));
}
+ @Override
+ long linkOriginalGenerator() {
+ if (originalsResolved) {
+ return 0;
+ }
+
+ long remaining = super.linkOriginalGenerator();
+ if (remaining == 0) {
+ for (Generator child : children) {
+ if (child instanceof AbstractExplicitGenerator) {
+ remaining += ((AbstractExplicitGenerator<?>) child).linkOriginalGenerator();
+ }
+ }
+ if (remaining == 0) {
+ originalsResolved = true;
+ }
+ }
+ return remaining;
+ }
+
@Override
final AbstractCompositeGenerator<?> getOriginal() {
return (AbstractCompositeGenerator<?>) super.getOriginal();
}
- final @NonNull AbstractExplicitGenerator<?> getOriginalChild(final QName childQName) {
+ final @NonNull OriginalLink getOriginalChild(final QName childQName) {
// First try groupings/augments ...
final AbstractExplicitGenerator<?> found = findInferredGenerator(childQName);
if (found != null) {
- return found;
+ return new Partial(found);
}
// ... no luck, we really need to start looking at our origin
final QName prevQName = childQName.bindTo(prev.getQName().getModule());
return verifyNotNull(prev.findSchemaTreeGenerator(prevQName),
- "Failed to find child %s (proxy for %s) in %s", prevQName, childQName, prev).getOriginal();
+ "Failed to find child %s (proxy for %s) in %s", prevQName, childQName, prev).originalLink();
}
@Override
package org.opendaylight.mdsal.binding.generator.impl.reactor;
import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
import com.google.common.base.MoreObjects.ToStringHelper;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.mdsal.binding.generator.impl.reactor.CollisionDomain.Member;
+import org.opendaylight.mdsal.binding.generator.impl.reactor.OriginalLink.Complete;
+import org.opendaylight.mdsal.binding.generator.impl.reactor.OriginalLink.Partial;
import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics;
import org.opendaylight.mdsal.binding.model.api.Type;
import org.opendaylight.mdsal.binding.model.api.TypeMemberComment;
private final @NonNull T statement;
- // FIXME: this, along with AbstractTypeObjectGenerator's (and TypedefGenerator's) fields should be better-controlled
- // with explicit sequencing guards. It it currently stands, we are expending two (or more) additional fields
- // to express downstream linking. If we had the concept of resolution step (an enum), we could just get by
- // with a simple queue of Step/Callback pairs, which would trigger as needed. For an example see how
- // AbstractTypeObjectGenerator manages baseGen/inferred fields.
- private AbstractExplicitGenerator<?> prev;
+ /**
+ * Field tracking previous incarnation (along reverse of 'uses' and 'augment' axis) of this statement. This field
+ * can either be one of:
+ * <ul>
+ * <li>{@code null} when not resolved, i.e. access is not legal, or</li>
+ * <li>{@code this} object if this is the original definition, or</li>
+ * <li>an {@link AbstractExplicitGenerator} pointing to the original definition, or</li>
+ * <li>a {@link Partial} link pointing to a generator closer to the original definition</li>
+ * </ul>
+ */
+ private Object prev;
AbstractExplicitGenerator(final T statement) {
this.statement = requireNonNull(statement);
return statement instanceof CopyableNode && ((CopyableNode) statement).isAugmenting();
}
- final void linkOriginalGenerator() {
- if (isAddedByUses() || isAugmenting()) {
- LOG.trace("Linking {}", this);
- prev = getParent().getOriginalChild(getQName());
+ /**
+ * Attempt to link the generator corresponding to the original definition for this generator's statements as well as
+ * to all child generators.
+ *
+ * @return Number of generators that remain unlinked.
+ */
+ long linkOriginalGenerator() {
+ final var local = prev;
+ if (local instanceof AbstractExplicitGenerator) {
+ return 0;
+ } else if (local instanceof Partial) {
+ return ((Partial) local).original() != null ? 0 : 1;
+ }
+ verify(local == null, "Unexpected link %s", local);
+
+ if (!isAddedByUses() && !isAugmenting()) {
+ prev = this;
+ LOG.trace("Linked {} to self", this);
+ return 0;
+ }
+
+ LOG.trace("Linking {}", this);
+ final var link = getParent().getOriginalChild(getQName());
+ if (link instanceof Complete) {
+ prev = ((Complete) link).original();
LOG.trace("Linked {} to {}", this, prev);
+ return 0;
}
+ prev = link;
+ LOG.trace("Linked {} to intermediate {}", this, prev);
+ return link.original() != null ? 0 : 1;
}
+ /**
+ * Return the previous incarnation of this generator, or {@code null} if this is the original generator.
+ *
+ * @return Previous incarnation or {@code null}
+ */
final @Nullable AbstractExplicitGenerator<?> previous() {
- return prev;
+ final var local = prev();
+ if (local == this) {
+ return null;
+ } else if (local instanceof Partial) {
+ return ((Partial) local).previous();
+ } else {
+ return verifyExplicit(local);
+ }
}
+ /**
+ * Return the original incarnation of this generator, or self if this is the original generator.
+ *
+ * @return Original incarnation of this generator
+ */
@NonNull AbstractExplicitGenerator<?> getOriginal() {
- return prev == null ? this : prev.getOriginal();
+ final var local = prev();
+ return local == this ? this : verifyExplicit(local).getOriginal();
+ }
+
+ /**
+ * Return the link towards the original generator.
+ *
+ * @return Link towards the original generator.
+ */
+ final @NonNull OriginalLink originalLink() {
+ final var local = prev;
+ if (local == null) {
+ return new Partial(this);
+ } else if (local == this) {
+ return new Complete(this);
+ } else if (local instanceof Partial) {
+ return (Partial) local;
+ } else {
+ return verifyExplicit(local).originalLink();
+ }
+ }
+
+ private static @NonNull AbstractExplicitGenerator<?> verifyExplicit(final Object prev) {
+ verify(prev instanceof AbstractExplicitGenerator, "Unexpected previous %s", prev);
+ return (AbstractExplicitGenerator<?>) prev;
+ }
+
+ private @NonNull Object prev() {
+ return verifyNotNull(prev, "Generator %s does not have linkage to previous instance resolved", this);
}
@Nullable AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
private final TypeEffectiveStatement<?> type;
+ // FIXME: these fields should be better-controlled with explicit sequencing guards. It it currently stands, we are
+ // expending two (or more) additional fields to express downstream linking. If we had the concept of
+ // resolution step (an enum), we could just get by with a simple queue of Step/Callback pairs, which would
+ // trigger as needed. See how we manage baseGen/inferred fields.
+
/**
* The generator corresponding to our YANG base type. It produces the superclass of our encapsulated type. If it is
* {@code null}, this generator is the root of the hierarchy.
}
// Step 1c: ... finally establish linkage along the reverse uses/augment axis. This is needed to route generated
- // type manifestations (isAddedByUses/isAugmenting) to their type generation sites.
- linkOriginalGenerator(children);
+ // type manifestations (isAddedByUses/isAugmenting) to their type generation sites. Since generator
+ // tree iteration order does not match dependencies, we may need to perform multiple passes.
+ long unlinkedOriginals = Long.MAX_VALUE;
+ do {
+ long remaining = 0;
+ for (ModuleGenerator module : children) {
+ remaining += module.linkOriginalGenerator();
+ }
+ verify(remaining < unlinkedOriginals, "Failed to make progress on linking of remaining %s originals",
+ remaining);
+ unlinkedOriginals = remaining;
+ } while (unlinkedOriginals != 0);
/*
* Step 2: link typedef statements, so that typedef's 'type' axis is fully established
}
}
- private static void linkOriginalGenerator(final Iterable<? extends Generator> parent) {
- for (Generator child : parent) {
- if (child instanceof AbstractExplicitGenerator) {
- ((AbstractExplicitGenerator<?>) child).linkOriginalGenerator();
- }
- if (child instanceof AbstractCompositeGenerator) {
- linkOriginalGenerator(child);
- }
- }
- }
-
private void linkDependencies(final Iterable<? extends Generator> parent) {
for (Generator child : parent) {
if (child instanceof AbstractDependentGenerator) {
--- /dev/null
+/*
+ * Copyright (c) 2022 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.mdsal.binding.generator.impl.reactor;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Link to the original definition of an {@link AbstractExplicitGenerator}.
+ */
+// FIXME: sealed when we have JDK17+
+abstract class OriginalLink {
+ static final class Complete extends OriginalLink {
+ private final @NonNull AbstractExplicitGenerator<?> original;
+
+ Complete(final AbstractExplicitGenerator<?> original) {
+ this.original = requireNonNull(original);
+ }
+
+ @Override
+ AbstractExplicitGenerator<?> previous() {
+ return original;
+ }
+
+ @Override
+ @NonNull AbstractExplicitGenerator<?> original() {
+ return original;
+ }
+
+ @Override
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return helper.add("original", original);
+ }
+ }
+
+ static final class Partial extends OriginalLink {
+ private final @NonNull AbstractExplicitGenerator<?> previous;
+ private AbstractExplicitGenerator<?> original;
+
+ Partial(final AbstractExplicitGenerator<?> previous) {
+ this.previous = requireNonNull(previous);
+ }
+
+ @Override
+ AbstractExplicitGenerator<?> previous() {
+ return previous;
+ }
+
+ @Override
+ AbstractExplicitGenerator<?> original() {
+ if (original == null) {
+ final var link = previous.originalLink();
+ if (link instanceof Complete || link.previous() != previous) {
+ original = link.original();
+ }
+ }
+ return original;
+ }
+
+ @Override
+ ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+ return helper.add("previous", previous).add("original", original);
+ }
+ }
+
+ private OriginalLink() {
+ // Hidden on purpose
+ }
+
+ abstract @NonNull AbstractExplicitGenerator<?> previous();
+
+ abstract @Nullable AbstractExplicitGenerator<?> original();
+
+ abstract ToStringHelper addToStringAttributes(ToStringHelper helper);
+
+ @Override
+ public final String toString() {
+ return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+ }
+}