2 * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.generator.impl;
10 import static com.google.common.base.Verify.verify;
12 import com.google.common.base.Stopwatch;
13 import com.google.common.collect.HashMultimap;
14 import com.google.common.collect.SetMultimap;
15 import java.util.HashMap;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.opendaylight.mdsal.binding.generator.impl.reactor.AbstractExplicitGenerator;
19 import org.opendaylight.mdsal.binding.generator.impl.reactor.Generator;
20 import org.opendaylight.mdsal.binding.generator.impl.reactor.GeneratorReactor;
21 import org.opendaylight.mdsal.binding.generator.impl.reactor.IdentityGenerator;
22 import org.opendaylight.mdsal.binding.generator.impl.reactor.ModuleGenerator;
23 import org.opendaylight.mdsal.binding.generator.impl.reactor.RpcGenerator;
24 import org.opendaylight.mdsal.binding.generator.impl.reactor.RpcInputGenerator;
25 import org.opendaylight.mdsal.binding.generator.impl.reactor.RpcOutputGenerator;
26 import org.opendaylight.mdsal.binding.generator.impl.reactor.TypeBuilderFactory;
27 import org.opendaylight.mdsal.binding.generator.impl.rt.DefaultBindingRuntimeTypes;
28 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
29 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
30 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
31 import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType;
32 import org.opendaylight.mdsal.binding.runtime.api.IdentityRuntimeType;
33 import org.opendaylight.mdsal.binding.runtime.api.InputRuntimeType;
34 import org.opendaylight.mdsal.binding.runtime.api.ModuleRuntimeType;
35 import org.opendaylight.mdsal.binding.runtime.api.OutputRuntimeType;
36 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
37 import org.opendaylight.yangtools.concepts.Mutable;
38 import org.opendaylight.yangtools.yang.common.QName;
39 import org.opendaylight.yangtools.yang.common.QNameModule;
40 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 final class BindingRuntimeTypesFactory implements Mutable {
45 private static final Logger LOG = LoggerFactory.getLogger(BindingRuntimeTypesFactory.class);
47 // Modules, indexed by their QNameModule
48 private final Map<QNameModule, ModuleRuntimeType> modules = new HashMap<>();
49 // Identities, indexed by their QName
50 private final Map<QName, IdentityRuntimeType> identities = new HashMap<>();
51 // All known types, indexed by their JavaTypeName
52 private final Map<JavaTypeName, RuntimeType> allTypes = new HashMap<>();
53 // All RpcOutputs, indexed by their RPC's QName
54 private final Map<QName, OutputRuntimeType> rpcOutputs = new HashMap<>();
55 // All RpcInputs, indexed by their RPC's QName
56 private final Map<QName, InputRuntimeType> rpcInputs = new HashMap<>();
57 // All known 'choice's to their corresponding cases
58 private final SetMultimap<JavaTypeName, CaseRuntimeType> choiceToCases = HashMultimap.create();
60 private BindingRuntimeTypesFactory() {
64 static @NonNull BindingRuntimeTypes createTypes(final @NonNull EffectiveModelContext context) {
65 final var moduleGens = new GeneratorReactor(context).execute(TypeBuilderFactory.runtime());
67 final Stopwatch sw = Stopwatch.createStarted();
68 final BindingRuntimeTypesFactory factory = new BindingRuntimeTypesFactory();
69 factory.indexModules(moduleGens);
70 LOG.debug("Indexed {} generators in {}", moduleGens.size(), sw);
72 return new DefaultBindingRuntimeTypes(context, factory.modules, factory.allTypes, factory.identities,
73 factory.rpcInputs, factory.rpcOutputs, factory.choiceToCases);
76 private void indexModules(final Map<QNameModule, ModuleGenerator> moduleGens) {
77 for (var entry : moduleGens.entrySet()) {
78 final var modGen = entry.getValue();
80 // index the module's runtime type
81 safePut(modules, "modules", entry.getKey(), modGen.runtimeType().orElseThrow());
83 // index module's identities and RPC input/outputs
84 for (var gen : modGen) {
85 if (gen instanceof IdentityGenerator) {
86 ((IdentityGenerator) gen).runtimeType().ifPresent(identity -> {
87 safePut(identities, "identities", identity.statement().argument(), identity);
90 // FIXME: do not collect these once we they generate a proper RuntimeType
91 if (gen instanceof RpcGenerator) {
92 final QName rpcName = ((RpcGenerator) gen).statement().argument();
93 for (var subgen : gen) {
94 if (subgen instanceof RpcInputGenerator) {
95 ((RpcInputGenerator) subgen).runtimeType()
96 .ifPresent(input -> rpcInputs.put(rpcName, input));
97 } else if (subgen instanceof RpcOutputGenerator) {
98 ((RpcOutputGenerator) subgen).runtimeType()
99 .ifPresent(output -> rpcOutputs.put(rpcName, output));
106 indexRuntimeTypes(moduleGens.values());
109 private void indexRuntimeTypes(final Iterable<? extends Generator> generators) {
110 for (Generator gen : generators) {
111 if (gen instanceof AbstractExplicitGenerator && gen.generatedType().isPresent()) {
112 final var type = ((AbstractExplicitGenerator<?, ?>) gen).runtimeType().orElseThrow();
113 final var javaType = type.javaType();
114 if (javaType instanceof GeneratedType) {
115 final var name = javaType.getIdentifier();
116 final var prev = allTypes.put(name, type);
117 verify(prev == null || prev == type, "Conflict on runtime type mapping of %s between %s and %s",
120 // Global indexing of cases generated for a particular choice. We look at the Generated type
121 // and make assumptions about its shape -- which works just fine without touching the
122 // ChoiceRuntimeType for cases.
123 if (type instanceof CaseRuntimeType) {
124 final var ifaces = ((GeneratedType) javaType).getImplements();
125 // The appropriate choice and DataObject at the very least. The choice interface is the first
127 verify(ifaces.size() >= 2, "Unexpected implemented interfaces %s", ifaces);
128 choiceToCases.put(ifaces.get(0).getIdentifier(), (CaseRuntimeType) type);
132 indexRuntimeTypes(gen);
136 private static <K, V> void safePut(final Map<K, V> map, final String name, final K key, final V value) {
137 final var prev = map.put(key, value);
138 verify(prev == null, "Conflict in %s, key %s conflicts on %s versus %s", name, key, prev, value);