X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-generator-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fgenerator%2Fimpl%2Freactor%2FAbstractCompositeGenerator.java;fp=binding%2Fmdsal-binding-generator-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fgenerator%2Fimpl%2Freactor%2FAbstractCompositeGenerator.java;h=9334af3c85254f40f64e49ef13dbf555d37c519c;hb=82684b43090a871c633a333b20fde09c9392fb17;hp=0000000000000000000000000000000000000000;hpb=0344fa76ca255551019735efbaf2dba675007813;p=mdsal.git diff --git a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractCompositeGenerator.java b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractCompositeGenerator.java new file mode 100644 index 0000000000..9334af3c85 --- /dev/null +++ b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractCompositeGenerator.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2021 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.Iterator; +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.Enumeration; +import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject; +import org.opendaylight.mdsal.binding.model.api.GeneratedType; +import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder; +import org.opendaylight.mdsal.binding.model.util.BindingTypes; +import org.opendaylight.yangtools.yang.common.QName; +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.ActionEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.AnydataEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.AnyxmlEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ContainerEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.GroupingEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.InputEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.LeafListEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement; +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.TypedefEffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement; +import org.opendaylight.yangtools.yang.model.ri.type.TypeBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A composite generator. Composite generators may contain additional children, which end up being mapped into + * the naming hierarchy 'under' the composite generator. To support this use case, each composite has a Java package + * name assigned. + */ +abstract class AbstractCompositeGenerator> extends AbstractExplicitGenerator { + private static final Logger LOG = LoggerFactory.getLogger(AbstractCompositeGenerator.class); + + private final @NonNull CollisionDomain domain = new CollisionDomain(); + private final List children; + + private List augments = List.of(); + private List groupings; + + AbstractCompositeGenerator(final T statement) { + super(statement); + children = createChildren(statement); + } + + AbstractCompositeGenerator(final T statement, final AbstractCompositeGenerator parent) { + super(statement, parent); + children = createChildren(statement); + } + + @Override + public final Iterator iterator() { + return children.iterator(); + } + + @Override + final boolean isEmpty() { + 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; + } + } + return null; + } + + final @NonNull CollisionDomain domain() { + return domain; + } + + 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 + final List 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); + } + } + } + } + groupings = List.copyOf(tmp); + } + + final void addAugment(final AbstractAugmentGenerator augment) { + if (augments.isEmpty()) { + augments = new ArrayList<>(2); + } + augments.add(requireNonNull(augment)); + } + + @Override + final AbstractCompositeGenerator getOriginal() { + return (AbstractCompositeGenerator) super.getOriginal(); + } + + final @NonNull AbstractExplicitGenerator getOriginalChild(final QName childQName) { + // First try groupings/augments ... + final AbstractExplicitGenerator found = findInferredGenerator(childQName); + if (found != null) { + return found; + } + + // ... no luck, we really need to start looking at our origin + final AbstractExplicitGenerator prev = verifyNotNull(previous(), + "Failed to find %s in scope of %s", childQName, this); + + 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(); + } + + @Override + final @Nullable AbstractExplicitGenerator findSchemaTreeGenerator(final QName qname) { + final AbstractExplicitGenerator found = super.findSchemaTreeGenerator(qname); + return found != null ? found : findInferredGenerator(qname); + } + + private @Nullable AbstractExplicitGenerator findInferredGenerator(final QName qname) { + // First search our local groupings ... + for (GroupingGenerator grouping : groupings) { + final AbstractExplicitGenerator gen = grouping.findSchemaTreeGenerator( + qname.bindTo(grouping.statement().argument().getModule())); + if (gen != null) { + return gen; + } + } + // ... next try local augments, which may have groupings themselves + for (AbstractAugmentGenerator augment : augments) { + final AbstractExplicitGenerator gen = augment.findSchemaTreeGenerator(qname); + if (gen != null) { + return gen; + } + } + return null; + } + + /** + * Update the specified builder to implement interfaces generated for the {@code grouping} statements this generator + * is using. + * + * @param builder Target builder + * @param builderFactory factory for creating {@link TypeBuilder}s + * @return The number of groupings this type uses. + */ + final int addUsesInterfaces(final GeneratedTypeBuilder builder, final TypeBuilderFactory builderFactory) { + for (GroupingGenerator grp : groupings) { + builder.addImplementsType(grp.getGeneratedType(builderFactory)); + } + return groupings.size(); + } + + static final void addAugmentable(final GeneratedTypeBuilder builder) { + builder.addImplementsType(BindingTypes.augmentable(builder)); + } + + final void addGetterMethods(final GeneratedTypeBuilder builder, final TypeBuilderFactory builderFactory) { + for (Generator child : this) { + // Only process explicit generators here + if (child instanceof AbstractExplicitGenerator) { + ((AbstractExplicitGenerator) child).addAsGetterMethod(builder, builderFactory); + } + + final GeneratedType enclosedType = child.enclosedType(builderFactory); + if (enclosedType instanceof GeneratedTransferObject) { + builder.addEnclosingTransferObject((GeneratedTransferObject) enclosedType); + } else if (enclosedType instanceof Enumeration) { + builder.addEnumeration((Enumeration) enclosedType); + } else { + verify(enclosedType == null, "Unhandled enclosed type %s in %s", enclosedType, child); + } + } + } + + private List createChildren(final EffectiveStatement statement) { + final List tmp = new ArrayList<>(); + final List tmpAug = new ArrayList<>(); + + for (EffectiveStatement stmt : statement.effectiveSubstatements()) { + if (stmt instanceof ActionEffectiveStatement) { + if (!isAugmenting(stmt)) { + tmp.add(new ActionGenerator((ActionEffectiveStatement) stmt, this)); + } + } else if (stmt instanceof AnydataEffectiveStatement) { + if (!isAugmenting(stmt)) { + tmp.add(new OpaqueObjectGenerator<>((AnydataEffectiveStatement) stmt, this)); + } + } else if (stmt instanceof AnyxmlEffectiveStatement) { + if (!isAugmenting(stmt)) { + tmp.add(new OpaqueObjectGenerator<>((AnyxmlEffectiveStatement) stmt, this)); + } + } else if (stmt instanceof CaseEffectiveStatement) { + tmp.add(new CaseGenerator((CaseEffectiveStatement) stmt, this)); + } else if (stmt instanceof ChoiceEffectiveStatement) { + // FIXME: use isOriginalDeclaration() ? + if (!isAddedByUses(stmt)) { + tmp.add(new ChoiceGenerator((ChoiceEffectiveStatement) stmt, this)); + } + } else if (stmt instanceof ContainerEffectiveStatement) { + if (isOriginalDeclaration(stmt)) { + tmp.add(new ContainerGenerator((ContainerEffectiveStatement) stmt, this)); + } + } else if (stmt instanceof GroupingEffectiveStatement) { + tmp.add(new GroupingGenerator((GroupingEffectiveStatement) stmt, this)); + } else if (stmt instanceof IdentityEffectiveStatement) { + tmp.add(new IdentityGenerator((IdentityEffectiveStatement) stmt, this)); + } else if (stmt instanceof InputEffectiveStatement) { + // FIXME: do not generate legacy RPC layout + tmp.add(this instanceof RpcGenerator ? new RpcContainerGenerator((InputEffectiveStatement) stmt, this) + : new OperationContainerGenerator((InputEffectiveStatement) stmt, this)); + } else if (stmt instanceof LeafEffectiveStatement) { + if (!isAugmenting(stmt)) { + tmp.add(new LeafGenerator((LeafEffectiveStatement) stmt, this)); + } + } else if (stmt instanceof LeafListEffectiveStatement) { + if (!isAugmenting(stmt)) { + tmp.add(new LeafListGenerator((LeafListEffectiveStatement) stmt, this)); + } + } else if (stmt instanceof ListEffectiveStatement) { + if (isOriginalDeclaration(stmt)) { + final ListGenerator listGen = new ListGenerator((ListEffectiveStatement) stmt, this); + tmp.add(listGen); + + final KeyGenerator keyGen = listGen.keyGenerator(); + if (keyGen != null) { + tmp.add(keyGen); + } + } + } else if (stmt instanceof NotificationEffectiveStatement) { + if (!isAugmenting(stmt)) { + tmp.add(new NotificationGenerator((NotificationEffectiveStatement) stmt, this)); + } + } else if (stmt instanceof OutputEffectiveStatement) { + // FIXME: do not generate legacy RPC layout + tmp.add(this instanceof RpcGenerator ? new RpcContainerGenerator((OutputEffectiveStatement) stmt, this) + : new OperationContainerGenerator((OutputEffectiveStatement) stmt, this)); + } else if (stmt instanceof RpcEffectiveStatement) { + tmp.add(new RpcGenerator((RpcEffectiveStatement) stmt, this)); + } else if (stmt instanceof TypedefEffectiveStatement) { + tmp.add(new TypedefGenerator((TypedefEffectiveStatement) stmt, this)); + } else if (stmt instanceof AugmentEffectiveStatement) { + if (this instanceof ModuleGenerator) { + tmpAug.add(new ModuleAugmentGenerator((AugmentEffectiveStatement) stmt, this)); + } + } else if (stmt instanceof UsesEffectiveStatement) { + final UsesEffectiveStatement uses = (UsesEffectiveStatement) stmt; + for (EffectiveStatement usesSub : uses.effectiveSubstatements()) { + if (usesSub instanceof AugmentEffectiveStatement) { + tmpAug.add(new UsesAugmentGenerator((AugmentEffectiveStatement) usesSub, this, uses)); + } + } + } else { + LOG.trace("Ignoring statement {}", stmt); + continue; + } + } + + // Sort augments and add them last. This ensures child iteration order always reflects potential + // interdependencies, hence we do not need to worry about them. + tmpAug.sort(AbstractAugmentGenerator.COMPARATOR); + tmp.addAll(tmpAug); + + // Compatibility FooService and FooListener interfaces, only generated for modules. + if (this instanceof ModuleGenerator) { + final ModuleGenerator moduleGen = (ModuleGenerator) this; + + final List notifs = tmp.stream() + .filter(NotificationGenerator.class::isInstance) + .map(NotificationGenerator.class::cast) + .collect(Collectors.toUnmodifiableList()); + if (!notifs.isEmpty()) { + tmp.add(new NotificationServiceGenerator(moduleGen, notifs)); + } + + final List rpcs = tmp.stream() + .filter(RpcGenerator.class::isInstance) + .map(RpcGenerator.class::cast) + .collect(Collectors.toUnmodifiableList()); + if (!rpcs.isEmpty()) { + tmp.add(new RpcServiceGenerator(moduleGen, rpcs)); + } + } + + return List.copyOf(tmp); + } + + // Utility equivalent of (!isAddedByUses(stmt) && !isAugmenting(stmt)). Takes advantage of relationship between + // CopyableNode and AddedByUsesAware + private static boolean isOriginalDeclaration(final EffectiveStatement stmt) { + if (stmt instanceof AddedByUsesAware) { + if (((AddedByUsesAware) stmt).isAddedByUses() + || stmt instanceof CopyableNode && ((CopyableNode) stmt).isAugmenting()) { + return false; + } + } + return true; + } + + private static boolean isAddedByUses(final EffectiveStatement stmt) { + return stmt instanceof AddedByUsesAware && ((AddedByUsesAware) stmt).isAddedByUses(); + } + + private static boolean isAugmenting(final EffectiveStatement stmt) { + return stmt instanceof CopyableNode && ((CopyableNode) stmt).isAugmenting(); + } +}