a3b8807c62f8c1d12849108a1fae3659176af4cb
[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 java.util.HashMap;
14 import java.util.Map;
15 import org.eclipse.jdt.annotation.NonNull;
16 import org.opendaylight.mdsal.binding.generator.impl.reactor.AbstractExplicitGenerator;
17 import org.opendaylight.mdsal.binding.generator.impl.reactor.Generator;
18 import org.opendaylight.mdsal.binding.generator.impl.reactor.GeneratorReactor;
19 import org.opendaylight.mdsal.binding.generator.impl.reactor.IdentityGenerator;
20 import org.opendaylight.mdsal.binding.generator.impl.reactor.ModuleGenerator;
21 import org.opendaylight.mdsal.binding.generator.impl.reactor.RpcGenerator;
22 import org.opendaylight.mdsal.binding.generator.impl.reactor.RpcInputGenerator;
23 import org.opendaylight.mdsal.binding.generator.impl.reactor.RpcOutputGenerator;
24 import org.opendaylight.mdsal.binding.generator.impl.reactor.TypeBuilderFactory;
25 import org.opendaylight.mdsal.binding.generator.impl.rt.DefaultBindingRuntimeTypes;
26 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
27 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
28 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
29 import org.opendaylight.mdsal.binding.runtime.api.IdentityRuntimeType;
30 import org.opendaylight.mdsal.binding.runtime.api.InputRuntimeType;
31 import org.opendaylight.mdsal.binding.runtime.api.ModuleRuntimeType;
32 import org.opendaylight.mdsal.binding.runtime.api.OutputRuntimeType;
33 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
34 import org.opendaylight.yangtools.concepts.Mutable;
35 import org.opendaylight.yangtools.yang.common.QName;
36 import org.opendaylight.yangtools.yang.common.QNameModule;
37 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 final class BindingRuntimeTypesFactory implements Mutable {
42     private static final Logger LOG = LoggerFactory.getLogger(BindingRuntimeTypesFactory.class);
43
44     // Modules, indexed by their QNameModule
45     private final Map<QNameModule, ModuleRuntimeType> modules = new HashMap<>();
46     // Identities, indexed by their QName
47     private final Map<QName, IdentityRuntimeType> identities = new HashMap<>();
48     // All known types, indexed by their JavaTypeName
49     private final Map<JavaTypeName, RuntimeType> allTypes = new HashMap<>();
50     // All RpcOutputs, indexed by their RPC's QName
51     private final Map<QName, OutputRuntimeType> rpcOutputs = new HashMap<>();
52     // All RpcInputs, indexed by their RPC's QName
53     private final Map<QName, InputRuntimeType> rpcInputs = new HashMap<>();
54
55     private BindingRuntimeTypesFactory() {
56         // Hidden on purpose
57     }
58
59     static @NonNull BindingRuntimeTypes createTypes(final @NonNull EffectiveModelContext context) {
60         final var moduleGens = new GeneratorReactor(context).execute(TypeBuilderFactory.runtime());
61
62         final Stopwatch sw = Stopwatch.createStarted();
63         final BindingRuntimeTypesFactory factory = new BindingRuntimeTypesFactory();
64         factory.indexModules(moduleGens);
65         LOG.debug("Indexed {} generators in {}", moduleGens.size(), sw);
66
67         return new DefaultBindingRuntimeTypes(context, factory.modules, factory.allTypes, factory.identities,
68             factory.rpcInputs, factory.rpcOutputs);
69     }
70
71     private void indexModules(final Map<QNameModule, ModuleGenerator> moduleGens) {
72         for (var entry : moduleGens.entrySet()) {
73             final var modGen = entry.getValue();
74
75             // index the module's runtime type
76             safePut(modules, "modules", entry.getKey(), modGen.runtimeType().orElseThrow());
77
78             // index module's identities and RPC input/outputs
79             for (var gen : modGen) {
80                 if (gen instanceof IdentityGenerator) {
81                     ((IdentityGenerator) gen).runtimeType().ifPresent(identity -> {
82                         safePut(identities, "identities", identity.statement().argument(), identity);
83                     });
84                 }
85                 // FIXME: do not collect these once we they generate a proper RuntimeType
86                 if (gen instanceof RpcGenerator) {
87                     final QName rpcName = ((RpcGenerator) gen).statement().argument();
88                     for (var subgen : gen) {
89                         if (subgen instanceof RpcInputGenerator) {
90                             ((RpcInputGenerator) subgen).runtimeType()
91                                 .ifPresent(input -> rpcInputs.put(rpcName, input));
92                         } else if (subgen instanceof RpcOutputGenerator) {
93                             ((RpcOutputGenerator) subgen).runtimeType()
94                                 .ifPresent(output -> rpcOutputs.put(rpcName, output));
95                         }
96                     }
97                 }
98             }
99         }
100
101         indexRuntimeTypes(moduleGens.values());
102     }
103
104     private void indexRuntimeTypes(final Iterable<? extends Generator> generators) {
105         for (Generator gen : generators) {
106             if (gen instanceof AbstractExplicitGenerator && gen.generatedType().isPresent()) {
107                 final var type = ((AbstractExplicitGenerator<?, ?>) gen).runtimeType().orElseThrow();
108                 final var javaType = type.javaType();
109                 if (javaType instanceof GeneratedType) {
110                     final var name = javaType.getIdentifier();
111                     final var prev = allTypes.put(name, type);
112                     verify(prev == null || prev == type, "Conflict on runtime type mapping of %s between %s and %s",
113                         name, prev, type);
114                 }
115             }
116             indexRuntimeTypes(gen);
117         }
118     }
119
120     private static <K, V> void safePut(final Map<K, V> map, final String name, final K key, final V value) {
121         final var prev = map.put(key, value);
122         verify(prev == null, "Conflict in %s, key %s conflicts on %s versus %s", name, key, prev, value);
123     }
124 }