/* * 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 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; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.concepts.Mutable; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; /** * Class tracking state of resolution of an {@code augment} statement's target generator. * *

* 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. Note we * take an initial hint -- which UsesAugmentGenerator provides, but ModuleAugmentGenerator does not -- and that * accounts for the difference. */ final class AugmentRequirement implements Mutable { private final @NonNull Set squashNamespaces = new HashSet<>(4); private final @NonNull AbstractAugmentGenerator augment; private final @NonNull Iterator remaining; private @NonNull AbstractCompositeGenerator target; private QNameModule localNamespace; private QName qname; private AugmentRequirement(final AbstractAugmentGenerator augment, final AbstractCompositeGenerator target) { this.augment = requireNonNull(augment); this.target = requireNonNull(target); remaining = augment.statement().argument().getNodeIdentifiers().iterator(); qname = remaining.next(); } AugmentRequirement(final ModuleAugmentGenerator augment, final ModuleGenerator module) { this((AbstractAugmentGenerator) augment, module); } AugmentRequirement(final UsesAugmentGenerator augment, final GroupingGenerator grouping) { this((AbstractAugmentGenerator) augment, grouping); // Starting in a grouping: squash namespace references to the grouping's namespace localNamespace = grouping.getQName().getModule(); squashNamespaces.add(qname.getModule()); } @NonNull LinkageProgress resolve() { return qname == null ? resolveAsTarget() : resolveAsChild(); } @Override public String toString() { return MoreObjects.toStringHelper(this).omitNullValues() .add("augment", augment) .add("target", target) .add("qname", qname) .toString(); } private @NonNull LinkageProgress resolveAsTarget() { // Resolved requirement, if we also have original we end resolution here and now final var original = target.tryOriginal(); if (original != null) { augment.setTargetGenerator(original); original.addAugment(augment); return LinkageProgress.DONE; } return LinkageProgress.NONE; } private @NonNull LinkageProgress resolveAsChild() { // First try local statements without adjustment var gen = target.findLocalSchemaTreeGenerator(qname); if (gen != null) { return progressTo(gen); } // Second try local augments, as those are guaranteed to match namespace exactly final var aug = target.findAugmentForGenerator(qname); if (aug != null) { return moveTo(aug); } // Third try local groupings, as those perform their own adjustment final var grp = target.findGroupingForGenerator(qname); if (grp != null) { squashNamespaces.add(qname.getModule()); localNamespace = grp.getQName().getModule(); return moveTo(grp); } // Lastly try local statements adjusted with namespace, if applicable gen = target.findLocalSchemaTreeGenerator(squashNamespaces.contains(qname.getModule()) ? qname.bindTo(verifyNotNull(localNamespace)) : qname); if (gen != null) { return progressTo(gen); } return LinkageProgress.NONE; } private @NonNull LinkageProgress moveTo(final @NonNull AbstractCompositeGenerator newTarget) { target = newTarget; return tryProgress(); } private @NonNull LinkageProgress progressTo(final @NonNull AbstractExplicitGenerator newTarget) { verify(newTarget instanceof AbstractCompositeGenerator, "Unexpected generator %s", newTarget); target = (AbstractCompositeGenerator) newTarget; qname = remaining.hasNext() ? remaining.next() : null; return tryProgress(); } private @NonNull LinkageProgress tryProgress() { final var progress = resolve(); return progress != LinkageProgress.NONE ? progress : LinkageProgress.SOME; } }