import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
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.NotificationEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement;
import org.opendaylight.yangtools.yang.model.ri.type.TypeBuilder;
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);
return children.isEmpty();
}
- @Override
- final @Nullable Generator findGenerator(final EffectiveStatement<?, ?> stmt) {
- for (Generator gen : children) {
- if (gen instanceof AbstractExplicitGenerator && ((AbstractExplicitGenerator<?>) gen).statement() == stmt) {
- return gen;
+ final @Nullable AbstractExplicitGenerator<?> findGenerator(final List<EffectiveStatement<?, ?>> stmtPath) {
+ return findGenerator(MatchStrategy.identity(), stmtPath, 0);
+ }
+
+ final @Nullable AbstractExplicitGenerator<?> findGenerator(final MatchStrategy childStrategy,
+ // TODO: Wouldn't this method be nicer with Deque<EffectiveStatement<?, ?>> ?
+ final List<EffectiveStatement<?, ?>> stmtPath, final int offset) {
+ final EffectiveStatement<?, ?> stmt = stmtPath.get(offset);
+
+ // Try direct children first, which is simple
+ AbstractExplicitGenerator<?> ret = childStrategy.findGenerator(stmt, children);
+ if (ret != null) {
+ final int next = offset + 1;
+ if (stmtPath.size() == next) {
+ // Final step, return child
+ return ret;
+ }
+ if (ret instanceof AbstractCompositeGenerator) {
+ // We know how to descend down
+ return ((AbstractCompositeGenerator<?>) ret).findGenerator(childStrategy, stmtPath, next);
+ }
+ // Yeah, don't know how to continue here
+ return null;
+ }
+
+ // At this point we are about to fork for augments or groupings. In either case only schema tree statements can
+ // be found this way. The fun part is that if we find a match and need to continue, we will use the same
+ // strategy for children as well. We now know that this (and subsequent) statements need to have a QName
+ // argument.
+ if (stmt instanceof SchemaTreeEffectiveStatement) {
+ // grouping -> uses instantiation changes the namespace to the local namespace of the uses site. We are
+ // going the opposite direction, hence we are changing namespace from local to the grouping's namespace.
+ for (GroupingGenerator gen : groupings) {
+ final MatchStrategy strat = MatchStrategy.grouping(gen);
+ ret = gen.findGenerator(strat, stmtPath, offset);
+ if (ret != null) {
+ return ret;
+ }
+ }
+
+ // All augments are dead simple: they need to match on argument (which we expect to be a QName)
+ final MatchStrategy strat = MatchStrategy.augment();
+ for (AbstractAugmentGenerator gen : augments) {
+ ret = gen.findGenerator(strat, stmtPath, offset);
+ if (ret != null) {
+ return ret;
+ }
}
}
return null;
}
final void linkUsesDependencies(final GeneratorContext context) {
- // We are establishing two linkages here:
- // - we are resolving 'uses' statements to their corresponding 'grouping' definitions
- // - we propagate those groupings as anchors to any augment statements
+ // We are resolving 'uses' statements to their corresponding 'grouping' definitions
final List<GroupingGenerator> tmp = new ArrayList<>();
for (EffectiveStatement<?, ?> stmt : statement().effectiveSubstatements()) {
if (stmt instanceof UsesEffectiveStatement) {
- final UsesEffectiveStatement uses = (UsesEffectiveStatement) stmt;
- final GroupingGenerator grouping = context.resolveTreeScoped(GroupingGenerator.class, uses.argument());
- tmp.add(grouping);
-
- for (Generator gen : this) {
- if (gen instanceof UsesAugmentGenerator) {
- ((UsesAugmentGenerator) gen).linkGroupingDependency(uses, grouping);
- }
- }
+ tmp.add(context.resolveTreeScoped(GroupingGenerator.class, ((UsesEffectiveStatement) stmt).argument()));
}
}
groupings = List.copyOf(tmp);
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 OriginalLink.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
- final @Nullable AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
+ final AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
final AbstractExplicitGenerator<?> found = super.findSchemaTreeGenerator(qname);
return found != null ? found : findInferredGenerator(qname);
}
return null;
}
+ final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final SchemaNodeIdentifier path) {
+ // This is not quite straightforward. 'path' works on top of schema tree, which is instantiated view. Since we
+ // do not generate duplicate instantiations along 'uses' path, findSchemaTreeGenerator() would satisfy our
+ // request by returning a child of the source 'grouping'.
+ //
+ // When that happens, our subsequent lookups need to adjust the namespace being looked up to the grouping's
+ // namespace... except for the case when the step is actually an augmentation, in which case we must not make
+ // that adjustment.
+ //
+ // Hence we deal with this lookup recursively, dropping namespace hints when we cross into groupings.
+ return resolveSchemaNode(path.getNodeIdentifiers().iterator(), null);
+ }
+
+ private @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final Iterator<QName> qnames,
+ final @Nullable QNameModule localNamespace) {
+ final QName qname = qnames.next();
+
+ // First try local augments, as those are guaranteed to match namespace exactly
+ for (AbstractAugmentGenerator augment : augments) {
+ final AbstractExplicitGenerator<?> gen = augment.findSchemaTreeGenerator(qname);
+ if (gen != null) {
+ return resolveNext(gen, qnames, null);
+ }
+ }
+
+ // Second try local groupings, as those perform their own adjustment
+ for (GroupingGenerator grouping : groupings) {
+ final QNameModule ns = grouping.statement().argument().getModule();
+ final AbstractExplicitGenerator<?> gen = grouping.findSchemaTreeGenerator(qname.bindTo(ns));
+ if (gen != null) {
+ return resolveNext(gen, qnames, ns);
+ }
+ }
+
+ // Lastly try local statements adjusted with namespace, if applicable
+ final QName lookup = localNamespace == null ? qname : qname.bindTo(localNamespace);
+ final AbstractExplicitGenerator<?> gen = verifyNotNull(super.findSchemaTreeGenerator(lookup),
+ "Failed to find %s as %s in %s", qname, lookup, this);
+ return resolveNext(gen, qnames, localNamespace);
+ }
+
+ private static @NonNull AbstractExplicitGenerator<?> resolveNext(final @NonNull AbstractExplicitGenerator<?> gen,
+ final Iterator<QName> qnames, final QNameModule localNamespace) {
+ if (qnames.hasNext()) {
+ verify(gen instanceof AbstractCompositeGenerator, "Unexpected generator %s", gen);
+ return ((AbstractCompositeGenerator<?>) gen).resolveSchemaNode(qnames, localNamespace);
+ }
+ return gen;
+ }
+
/**
* Update the specified builder to implement interfaces generated for the {@code grouping} statements this generator
* is using.
} else if (stmt instanceof TypedefEffectiveStatement) {
tmp.add(new TypedefGenerator((TypedefEffectiveStatement) stmt, this));
} else if (stmt instanceof AugmentEffectiveStatement) {
+ // FIXME: MDSAL-695: So here we are ignoring any augment which is not in a module, while the 'uses'
+ // processing takes care of the rest. There are two problems here:
+ //
+ // 1) this could be an augment introduced through uses -- in this case we are picking
+ // confusing it with this being its declaration site, we should probably be
+ // ignoring it, but then
+ //
+ // 2) we are losing track of AugmentEffectiveStatement for which we do not generate
+ // interfaces -- and recover it at runtime through explicit walk along the
+ // corresponding AugmentationSchemaNode.getOriginalDefinition() pointer
+ //
+ // So here is where we should decide how to handle this augment, and make sure we
+ // retain information about this being an alias. That will serve as the base for keys
+ // in the augment -> original map we provide to BindingRuntimeTypes.
if (this instanceof ModuleGenerator) {
tmpAug.add(new ModuleAugmentGenerator((AugmentEffectiveStatement) stmt, this));
}
final UsesEffectiveStatement uses = (UsesEffectiveStatement) stmt;
for (EffectiveStatement<?, ?> usesSub : uses.effectiveSubstatements()) {
if (usesSub instanceof AugmentEffectiveStatement) {
- tmpAug.add(new UsesAugmentGenerator((AugmentEffectiveStatement) usesSub, this, uses));
+ tmpAug.add(new UsesAugmentGenerator((AugmentEffectiveStatement) usesSub, this));
}
}
} else {