import static java.util.Objects.requireNonNull;
import com.google.common.base.MoreObjects.ToStringHelper;
+import java.util.Optional;
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.tree.StatementRepresentation;
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;
import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotableTypeBuilder;
import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
+import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
import org.opendaylight.yangtools.yang.common.AbstractQName;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
import org.opendaylight.yangtools.yang.model.api.CopyableNode;
import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An explicit {@link Generator}, associated with a particular {@link EffectiveStatement}.
*/
-public abstract class AbstractExplicitGenerator<T extends EffectiveStatement<?, ?>> extends Generator
- implements CopyableNode {
+public abstract class AbstractExplicitGenerator<S extends EffectiveStatement<?, ?>, R extends RuntimeType>
+ extends Generator implements CopyableNode, StatementRepresentation<S> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractExplicitGenerator.class);
- private final @NonNull T statement;
+ private final @NonNull S 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>a generator which is one step closer to the original definition</li>
+ * </ul>
+ */
+ private AbstractExplicitGenerator<S, R> prev;
+ /**
+ * Field holding the original incarnation, i.e. the terminal node along {@link #prev} links.
+ */
+ private AbstractExplicitGenerator<S, R> orig;
+ /**
+ * Field containing and indicator holding the runtime type, if applicable.
+ */
+ private @Nullable R runtimeType;
+ private boolean runtimeTypeInitialized;
- AbstractExplicitGenerator(final T statement) {
+ AbstractExplicitGenerator(final S statement) {
this.statement = requireNonNull(statement);
}
- AbstractExplicitGenerator(final T statement, final AbstractCompositeGenerator<?> parent) {
+ AbstractExplicitGenerator(final S statement, final AbstractCompositeGenerator<?, ?> parent) {
super(parent);
this.statement = requireNonNull(statement);
}
+ @Override
+ public final @NonNull S statement() {
+ return statement;
+ }
+
/**
- * Return the {@link EffectiveStatement} associated with this generator.
+ * Return the {@link RuntimeType} associated with this object, of applicable.
*
- * @return An EffectiveStatement
+ * @return Associated run-time type, or empty
*/
- public final @NonNull T statement() {
- return statement;
+ public final Optional<R> runtimeType() {
+ if (!runtimeTypeInitialized) {
+ runtimeType = createRuntimeType();
+ runtimeTypeInitialized = true;
+ }
+ return Optional.ofNullable(runtimeType);
+ }
+
+ final Optional<R> runtimeTypeOf(final @NonNull S stmt) {
+ var gen = this;
+ do {
+ final var ret = gen.runtimeType();
+ if (ret.isPresent()) {
+ return Optional.of(rebaseRuntimeType(ret.orElseThrow(), stmt));
+ }
+
+ gen = gen.previous();
+ } while (gen != null);
+
+ return Optional.empty();
}
+ abstract @Nullable R createRuntimeType();
+
+ abstract @NonNull R rebaseRuntimeType(@NonNull R type, @NonNull S statement);
+
@Override
public final boolean isAddedByUses() {
return statement instanceof AddedByUsesAware && ((AddedByUsesAware) statement).isAddedByUses();
return statement instanceof CopyableNode && ((CopyableNode) statement).isAugmenting();
}
- final void linkOriginalGenerator(final GeneratorContext context) {
- if (isAddedByUses() || isAugmenting()) {
+ /**
+ * Attempt to link the generator corresponding to the original definition for this generator.
+ *
+ * @return {@code true} if this generator is linked
+ */
+ final boolean linkOriginalGenerator() {
+ if (orig != null) {
+ // Original already linked
+ return true;
+ }
+
+ if (prev == null) {
LOG.trace("Linking {}", this);
- prev = getParent().getOriginalChild(getQName());
- LOG.trace("Linked {} to {}", this, prev);
+
+ if (!isAddedByUses() && !isAugmenting()) {
+ orig = prev = this;
+ LOG.trace("Linked {} to self", this);
+ return true;
+ }
+
+ final var link = getParent().<S, R>originalChild(getQName());
+ if (link == null) {
+ LOG.trace("Cannot link {} yet", this);
+ return false;
+ }
+
+ prev = link.previous();
+ orig = link.original();
+ if (orig != null) {
+ LOG.trace("Linked {} to {} original {}", this, prev, orig);
+ return true;
+ }
+
+ LOG.trace("Linked {} to intermediate {}", this, prev);
+ return false;
}
+
+ orig = prev.originalLink().original();
+ if (orig != null) {
+ LOG.trace("Linked {} to original {}", this, orig);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 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<S, R> previous() {
+ final var local = verifyNotNull(prev, "Generator %s does not have linkage to previous instance resolved", this);
+ return local == this ? null : local;
+ }
+
+ /**
+ * Return the original incarnation of this generator, or self if this is the original generator.
+ *
+ * @return Original incarnation of this generator
+ */
+ @NonNull AbstractExplicitGenerator<S, R> getOriginal() {
+ return verifyNotNull(orig, "Generator %s does not have linkage to original instance resolved", this);
+ }
+
+ @Nullable AbstractExplicitGenerator<S, R> tryOriginal() {
+ return orig;
}
- final @Nullable AbstractExplicitGenerator<?> previous() {
- return prev;
+ /**
+ * Return the link towards the original generator.
+ *
+ * @return Link towards the original generator.
+ */
+ final @NonNull OriginalLink<S, R> originalLink() {
+ final var local = prev;
+ if (local == null) {
+ return OriginalLink.partial(this);
+ } else if (local == this) {
+ return OriginalLink.complete(this);
+ } else {
+ return OriginalLink.partial(local);
+ }
}
- @NonNull AbstractExplicitGenerator<?> getOriginal() {
- return prev == null ? this : prev.getOriginal();
+ @Nullable AbstractExplicitGenerator<?, ?> findSchemaTreeGenerator(final QName qname) {
+ return findLocalSchemaTreeGenerator(qname);
}
- @Nullable AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
+ final @Nullable AbstractExplicitGenerator<?, ?> findLocalSchemaTreeGenerator(final QName qname) {
for (Generator child : this) {
if (child instanceof AbstractExplicitGenerator) {
- final AbstractExplicitGenerator<?> gen = (AbstractExplicitGenerator<?>) child;
+ final AbstractExplicitGenerator<?, ?> gen = (AbstractExplicitGenerator<?, ?>) child;
final EffectiveStatement<?, ?> stmt = gen.statement();
if (stmt instanceof SchemaTreeEffectiveStatement && qname.equals(stmt.argument())) {
return gen;
return null;
}
- final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final @NonNull SchemaNodeIdentifier path,
- final @Nullable QNameModule targetModule) {
- AbstractExplicitGenerator<?> current = this;
- QNameModule currentModule = targetModule;
-
- for (QName next : path.getNodeIdentifiers()) {
- final QName qname = currentModule == null ? next : next.bindTo(currentModule);
- current = verifyNotNull(current.findSchemaTreeGenerator(qname),
- "Failed to find %s as %s in %s", next, qname, current);
-
- final QNameModule foundNamespace = current.getQName().getModule();
- if (!foundNamespace.equals(qname.getModule())) {
- // We have located a different QName than the one we were looking for. We need to make sure we adjust
- // all subsequent QNames to this new namespace
- currentModule = foundNamespace;
- }
- }
-
- return current;
- }
-
final @NonNull QName getQName() {
final Object arg = statement.argument();
verify(arg instanceof QName, "Unexpected argument %s", arg);