/* * 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 java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.binding.model.api.GeneratedType; import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType; import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType; import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType; import org.opendaylight.mdsal.binding.runtime.api.RuntimeType; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement; abstract class CompositeRuntimeTypeBuilder, R extends CompositeRuntimeType> { private final List augmentTypes = new ArrayList<>(); private final List childTypes = new ArrayList<>(); private final @NonNull S statement; CompositeRuntimeTypeBuilder(final S statement) { this.statement = requireNonNull(statement); } final @NonNull S statement() { return statement; } final @NonNull List getCaseChilden() { return childTypes.stream() .map(child -> { verify(child instanceof CaseRuntimeType, "Unexpected child %s in %s", child, statement); return (CaseRuntimeType) child; }) .collect(Collectors.toUnmodifiableList()); } final @NonNull R build(final @NonNull GeneratedType generatedType) { return build(generatedType, statement, childTypes, augmentTypes); } abstract @NonNull R build(GeneratedType type, S statement, List children, List augments); CompositeRuntimeTypeBuilder fillTypes(final ChildLookup lookup, final AbstractCompositeGenerator generator) { // Figure out which augments are valid in target statement and record their RuntimeTypes. // We will pass the latter to create method. We will use the former to perform replacement lookups instead // of 'this.augments'. That is necessary because 'this.augments' holds all augments targeting the GeneratedType, // hence equivalent augmentations from differing places would match our lookup and the reverse search would be // lead astray. // // Augments targeting 'choice' statement are handled by a separate class and need to be skipped here if (!(generator instanceof ChoiceGenerator)) { for (var augment : generator.augments()) { final var augmentRuntimeType = augment.runtimeTypeIn(lookup, statement); if (augmentRuntimeType != null) { augmentTypes.add(augmentRuntimeType); } } } // Now construct RuntimeTypes for each schema tree child of stmt for (var stmt : statement.effectiveSubstatements()) { if (stmt instanceof SchemaTreeEffectiveStatement) { final var child = (SchemaTreeEffectiveStatement) stmt; final var qname = child.getIdentifier(); // Try valid augments first: they should be empty most of the time and filter all the cases where we // would not find the streamChild among our local and grouping statements. Note that unlike all others, // such matches are not considered to be children in Binding DataObject tree, they are only considered // such in the schema tree. if (isAugmentedChild(lookup, qname)) { continue; } final var childRuntimeType = findChildRuntimeType(lookup, generator, child); if (childRuntimeType != null) { childTypes.add(childRuntimeType); } } } return this; } @SuppressWarnings("unchecked") final > @Nullable RuntimeType findChildRuntimeType( final @NonNull ChildLookup lookup, final AbstractCompositeGenerator parent, final @NonNull X stmt) { final var qname = stmt.getIdentifier(); // First try our local items without adjustment ... @SuppressWarnings("rawtypes") AbstractExplicitGenerator childGen = findChild(parent, qname); if (childGen == null) { // No luck, let's see if any of the groupings can find it for (GroupingGenerator grouping : parent.groupings()) { final var gen = grouping.findSchemaTreeGenerator( qname.bindTo(grouping.statement().argument().getModule())); if (gen != null) { return findChildRuntimeType(lookup.inGrouping(qname, grouping), grouping, stmt); } } // Finally attempt to find adjusted QName: this has to succeed final var adjusted = lookup.adjustQName(qname); childGen = verifyNotNull(findChild(parent, adjusted), "Failed to find %s as %s in %s", stmt, adjusted, this); } return childGen.createInternalRuntimeType(lookup, stmt); } boolean isAugmentedChild(final ChildLookup lookup, final QName qname) { // Note we are dealing with two different kinds of augments and they behave differently with respect // to namespaces. Top-level augments do not make an adjustment, while uses-augments do. return augmentTypes.stream().anyMatch(augment -> augment.schemaTreeChild(qname) != null); } private static @Nullable AbstractExplicitGenerator findChild(final AbstractCompositeGenerator parent, final QName qname) { for (var child : parent) { if (child instanceof AbstractExplicitGenerator) { final AbstractExplicitGenerator gen = (AbstractExplicitGenerator) child; final EffectiveStatement stmt = gen.statement(); if (stmt instanceof SchemaTreeEffectiveStatement && qname.equals(stmt.argument())) { return gen; } } } return null; } }