Bump to odlparent-9.0.0/yangtools-7.0.1-SNAPSHOT
[mdsal.git] / binding / mdsal-binding-generator-impl / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / AbstractCompositeGenerator.java
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 (file)
index 0000000..9334af3
--- /dev/null
@@ -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<T extends EffectiveStatement<?, ?>> extends AbstractExplicitGenerator<T> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractCompositeGenerator.class);
+
+    private final @NonNull CollisionDomain domain = new CollisionDomain();
+    private final List<Generator> children;
+
+    private List<AbstractAugmentGenerator> augments = List.of();
+    private List<GroupingGenerator> 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<Generator> 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<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);
+                    }
+                }
+            }
+        }
+        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<Generator> createChildren(final EffectiveStatement<?, ?> statement) {
+        final List<Generator> tmp = new ArrayList<>();
+        final List<AbstractAugmentGenerator> 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<NotificationGenerator> 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<RpcGenerator> 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();
+    }
+}