Remove BindingRuntimeTypes.findRpc{Input,Output}
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / BindingRuntimeTypesFactory.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.generator.impl;
9
10 import static com.google.common.base.Verify.verify;
11
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;
16 import java.util.Map;
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.TypeBuilderFactory;
24 import org.opendaylight.mdsal.binding.generator.impl.rt.DefaultBindingRuntimeTypes;
25 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
26 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
27 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
28 import org.opendaylight.mdsal.binding.runtime.api.CaseRuntimeType;
29 import org.opendaylight.mdsal.binding.runtime.api.IdentityRuntimeType;
30 import org.opendaylight.mdsal.binding.runtime.api.ModuleRuntimeType;
31 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
32 import org.opendaylight.yangtools.concepts.Mutable;
33 import org.opendaylight.yangtools.yang.common.QName;
34 import org.opendaylight.yangtools.yang.common.QNameModule;
35 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 final class BindingRuntimeTypesFactory implements Mutable {
40     private static final Logger LOG = LoggerFactory.getLogger(BindingRuntimeTypesFactory.class);
41
42     // Modules, indexed by their QNameModule
43     private final Map<QNameModule, ModuleRuntimeType> modules = new HashMap<>();
44     // Identities, indexed by their QName
45     private final Map<QName, IdentityRuntimeType> identities = new HashMap<>();
46     // All known types, indexed by their JavaTypeName
47     private final Map<JavaTypeName, RuntimeType> allTypes = new HashMap<>();
48     // All known 'choice's to their corresponding cases
49     private final SetMultimap<JavaTypeName, CaseRuntimeType> choiceToCases = HashMultimap.create();
50
51     private BindingRuntimeTypesFactory() {
52         // Hidden on purpose
53     }
54
55     static @NonNull BindingRuntimeTypes createTypes(final @NonNull EffectiveModelContext context) {
56         final var moduleGens = new GeneratorReactor(context).execute(TypeBuilderFactory.runtime());
57
58         final Stopwatch sw = Stopwatch.createStarted();
59         final BindingRuntimeTypesFactory factory = new BindingRuntimeTypesFactory();
60         factory.indexModules(moduleGens);
61         LOG.debug("Indexed {} generators in {}", moduleGens.size(), sw);
62
63         return new DefaultBindingRuntimeTypes(context, factory.modules, factory.allTypes, factory.identities,
64             factory.choiceToCases);
65     }
66
67     private void indexModules(final Map<QNameModule, ModuleGenerator> moduleGens) {
68         for (var entry : moduleGens.entrySet()) {
69             final var modGen = entry.getValue();
70
71             // index the module's runtime type
72             safePut(modules, "modules", entry.getKey(), modGen.runtimeType().orElseThrow());
73
74             // index module's identities and RPC input/outputs
75             for (var gen : modGen) {
76                 if (gen instanceof IdentityGenerator idGen) {
77                     idGen.runtimeType().ifPresent(identity -> {
78                         safePut(identities, "identities", identity.statement().argument(), identity);
79                     });
80                 }
81             }
82         }
83
84         indexRuntimeTypes(moduleGens.values());
85     }
86
87     private void indexRuntimeTypes(final Iterable<? extends Generator> generators) {
88         for (Generator gen : generators) {
89             if (gen instanceof AbstractExplicitGenerator<?, ?> explicitGen && gen.generatedType().isPresent()) {
90                 final var type = explicitGen.runtimeType().orElseThrow();
91                 if (type.javaType() instanceof GeneratedType genType) {
92                     final var name = genType.getIdentifier();
93                     final var prev = allTypes.put(name, type);
94                     verify(prev == null || prev == type, "Conflict on runtime type mapping of %s between %s and %s",
95                         name, prev, type);
96
97                     // Global indexing of cases generated for a particular choice. We look at the Generated type
98                     // and make assumptions about its shape -- which works just fine without touching the
99                     // ChoiceRuntimeType for cases.
100                     if (type instanceof CaseRuntimeType caseType) {
101                         final var ifaces = genType.getImplements();
102                         // The appropriate choice and DataObject at the very least. The choice interface is the first
103                         // one mentioned.
104                         verify(ifaces.size() >= 2, "Unexpected implemented interfaces %s", ifaces);
105                         choiceToCases.put(ifaces.get(0).getIdentifier(), caseType);
106                     }
107                 }
108             }
109             indexRuntimeTypes(gen);
110         }
111     }
112
113     private static <K, V> void safePut(final Map<K, V> map, final String name, final K key, final V value) {
114         final var prev = map.put(key, value);
115         verify(prev == null, "Conflict in %s, key %s conflicts on %s versus %s", name, key, prev, value);
116     }
117 }