Improve RpcRoutingStrategyTest
[mdsal.git] / binding / mdsal-binding-generator-impl / src / main / java / org / opendaylight / mdsal / binding / generator / impl / ModuleContext.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. 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.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.collect.BiMap;
14 import com.google.common.collect.HashBiMap;
15 import com.google.common.collect.HashMultimap;
16 import com.google.common.collect.Maps;
17 import com.google.common.collect.Multimap;
18 import com.google.common.collect.Multimaps;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
29 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
30 import org.opendaylight.mdsal.binding.model.api.Type;
31 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
32 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
33 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
34 import org.opendaylight.yangtools.concepts.Mutable;
35 import org.opendaylight.yangtools.yang.common.QName;
36 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
39 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
41 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
42 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.Module;
44 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
45 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
46 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
48 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 /**
53  * Utility class for building up Binding mapping information. This class is NOT thread-safe.
54  */
55 // FIXME: 8.0.0: hide this class
56 public final class ModuleContext implements Mutable {
57     private static final Logger LOG = LoggerFactory.getLogger(ModuleContext.class);
58
59     private final BiMap<Type, AugmentationSchemaNode> typeToAugmentation = HashBiMap.create();
60     private final Map<SchemaPath, GeneratedTypeBuilder> childNodes = new HashMap<>();
61     private final Map<SchemaPath, GeneratedTypeBuilder> groupings = new HashMap<>();
62     private final BiMap<Type, CaseSchemaNode> caseTypeToSchema = HashBiMap.create();
63     private final Map<SchemaPath, GeneratedTypeBuilder> cases = new HashMap<>();
64     private final Map<QName, GeneratedTypeBuilder> identities = new HashMap<>();
65     private final List<GeneratedTypeBuilder> augmentations = new ArrayList<>();
66     private final Multimap<Type, Type> choiceToCases = HashMultimap.create();
67     private final Set<GeneratedTypeBuilder> topLevelNodes = new HashSet<>();
68     private final Map<SchemaPath, JavaTypeName> aliases = new HashMap<>();
69     private final Map<Type, WithStatus> typeToSchema = new HashMap<>();
70     private final List<GeneratedTOBuilder> genTOs = new ArrayList<>();
71     private final Map<SchemaPath, Type> innerTypes = new HashMap<>();
72     private final Map<SchemaPath, GeneratedType> typedefs = new HashMap<>();
73     private final Module module;
74
75     // Conflict mapping
76     private final Map<JavaTypeName, SchemaNode> nameMapping = new HashMap<>();
77
78     private GeneratedTypeBuilder moduleNode;
79     private JavaTypeName moduleInfoType;
80     private String modulePackageName;
81
82     ModuleContext(final Module module) {
83         this.module = requireNonNull(module);
84     }
85
86     Module module() {
87         return module;
88     }
89
90     @NonNull String modulePackageName() {
91         String ret = modulePackageName;
92         if (ret == null) {
93             modulePackageName = ret = BindingMapping.getRootPackageName(module.getQNameModule());
94         }
95         return ret;
96     }
97
98     @NonNull JavaTypeName moduleInfoType() {
99         JavaTypeName ret = moduleInfoType;
100         if (ret == null) {
101             moduleInfoType = ret = JavaTypeName.create(modulePackageName(), BindingMapping.MODULE_INFO_CLASS_NAME);
102         }
103         return ret;
104     }
105
106     List<GeneratedType> getGeneratedTypes() {
107         List<GeneratedType> result = new ArrayList<>();
108
109         if (moduleNode != null) {
110             result.add(moduleNode.build());
111         }
112
113         for (GeneratedTOBuilder b : genTOs) {
114             result.add(b.build());
115         }
116         for (GeneratedType b : typedefs.values()) {
117             if (b != null) {
118                 result.add(b);
119             }
120         }
121         for (GeneratedTypeBuilder b : childNodes.values()) {
122             result.add(b.build());
123         }
124         for (GeneratedTypeBuilder b : groupings.values()) {
125             result.add(b.build());
126         }
127         for (GeneratedTypeBuilder b : cases.values()) {
128             result.add(b.build());
129         }
130         for (GeneratedTypeBuilder b : identities.values()) {
131             result.add(b.build());
132         }
133         for (GeneratedTypeBuilder b : topLevelNodes) {
134             result.add(b.build());
135         }
136         for (GeneratedTypeBuilder b : augmentations) {
137             result.add(b.build());
138         }
139         return result;
140     }
141
142     public Multimap<Type, Type> getChoiceToCases() {
143         return Multimaps.unmodifiableMultimap(choiceToCases);
144     }
145
146     public GeneratedTypeBuilder getModuleNode() {
147         return moduleNode;
148     }
149
150     public GeneratedTypeBuilder getChildNode(final SchemaPath path) {
151         return childNodes.get(path);
152     }
153
154     public GeneratedTypeBuilder getGrouping(final SchemaPath path) {
155         return groupings.get(path);
156     }
157
158     public GeneratedTypeBuilder getCase(final SchemaPath path) {
159         return cases.get(path);
160     }
161
162     public void addModuleNode(final GeneratedTypeBuilder newModuleNode) {
163         this.moduleNode = newModuleNode;
164     }
165
166     public void addGeneratedTOBuilder(final GeneratedTOBuilder builder) {
167         genTOs.add(builder);
168     }
169
170     @NonNull GeneratedType addAliasType(final ModuleContext sourceContext, final ContainerLike source,
171             final ContainerLike alias) {
172         final GeneratedTypeBuilder builder = sourceContext.getChildNode(source.getPath());
173         checkState(builder != null, "Could not find builder for %s", source);
174
175         final JavaTypeName id = builder.getIdentifier();
176         final SchemaPath path = alias.getPath();
177         final JavaTypeName prev = aliases.putIfAbsent(path, id);
178         checkState(prev == null, "Type aliasing conflict on %s: %s vs %s", path, prev, id);
179
180         return builder.build();
181     }
182
183     @Nullable JavaTypeName getAlias(final SchemaPath path) {
184         return aliases.get(path);
185     }
186
187     public void addChildNodeType(final SchemaNode def, final GeneratedTypeBuilder builder) {
188         checkNamingConflict(def, builder.getIdentifier());
189         childNodes.put(def.getPath(), builder);
190         typeToSchema.put(builder, def);
191     }
192
193     public void addGroupingType(final GroupingDefinition def, final GeneratedTypeBuilder builder) {
194         checkNamingConflict(def, builder.getIdentifier());
195         groupings.put(def.getPath(), builder);
196     }
197
198     public void addTypedefType(final TypeDefinition<?> def, final GeneratedType type) {
199         final JavaTypeName name = type.getIdentifier();
200         final SchemaNode existingDef = nameMapping.putIfAbsent(name, def);
201         if (existingDef != null) {
202             if (!(existingDef instanceof TypeDefinition)) {
203                 throw resolveNamingConfict(existingDef, def, name);
204             }
205
206             // This seems to be fine
207             LOG.debug("GeneratedType conflict between {} and {} on {}", def, existingDef, name);
208         }
209
210         typedefs.put(def.getPath(), type);
211     }
212
213     public void addCaseType(final SchemaPath path, final GeneratedTypeBuilder builder) {
214         cases.put(path, builder);
215     }
216
217     public void addIdentityType(final IdentitySchemaNode def, final GeneratedTypeBuilder builder) {
218         checkNamingConflict(def, builder.getIdentifier());
219         identities.put(def.getQName(), builder);
220     }
221
222     public void addTopLevelNodeType(final GeneratedTypeBuilder builder) {
223         topLevelNodes.add(builder);
224     }
225
226     public void addAugmentType(final GeneratedTypeBuilder builder) {
227         augmentations.add(builder);
228     }
229
230     public Map<SchemaPath, GeneratedType> getTypedefs() {
231         return typedefs;
232     }
233
234     public Map<SchemaPath, GeneratedTypeBuilder> getChildNodes() {
235         return Collections.unmodifiableMap(childNodes);
236     }
237
238     public Map<SchemaPath, GeneratedTypeBuilder> getGroupings() {
239         return Collections.unmodifiableMap(groupings);
240     }
241
242     public Map<SchemaPath, GeneratedTypeBuilder> getCases() {
243         return Collections.unmodifiableMap(cases);
244     }
245
246     public Map<QName, GeneratedTypeBuilder> getIdentities() {
247         return Collections.unmodifiableMap(identities);
248     }
249
250     public Set<GeneratedTypeBuilder> getTopLevelNodes() {
251         return Collections.unmodifiableSet(topLevelNodes);
252     }
253
254     public List<GeneratedTypeBuilder> getAugmentations() {
255         return Collections.unmodifiableList(augmentations);
256     }
257
258     public BiMap<Type, AugmentationSchemaNode> getTypeToAugmentation() {
259         return Maps.unmodifiableBiMap(typeToAugmentation);
260     }
261
262     public void addTypeToAugmentation(final GeneratedTypeBuilder builder, final AugmentationSchemaNode schema) {
263         typeToAugmentation.put(builder, schema);
264         typeToSchema.put(builder, schema);
265     }
266
267     public void addChoiceToCaseMapping(final Type choiceType, final Type caseType, final CaseSchemaNode schema) {
268         choiceToCases.put(choiceType, caseType);
269         caseTypeToSchema.put(caseType, schema);
270         typeToSchema.put(caseType, schema);
271     }
272
273     public BiMap<Type, CaseSchemaNode> getCaseTypeToSchemas() {
274         return Maps.unmodifiableBiMap(caseTypeToSchema);
275     }
276
277     /**
278      * Returns mapping of type to its schema. Valid values are only instances of {@link DataSchemaNode}
279      * or {@link AugmentationSchemaNode}.
280      *
281      * @return Mapping from type to corresponding schema
282      */
283     public Map<Type, WithStatus> getTypeToSchema() {
284         return Collections.unmodifiableMap(typeToSchema);
285     }
286
287     protected void addTypeToSchema(final Type type, final TypeDefinition<?> typedef) {
288         typeToSchema.put(type, typedef);
289     }
290
291     /**
292      * Adds mapping between schema path and an inner type.
293      */
294     void addInnerTypedefType(final SchemaPath path, final Type type) {
295         innerTypes.put(path, type);
296     }
297
298     public Type getInnerType(final SchemaPath path) {
299         return innerTypes.get(path);
300     }
301
302     private void checkNamingConflict(final SchemaNode def, final JavaTypeName name) {
303         final SchemaNode existingDef = nameMapping.putIfAbsent(name, def);
304         if (existingDef != null) {
305             if (def.equals(existingDef)) {
306                 if (LOG.isDebugEnabled()) {
307                     // TODO: this should not really be happening
308                     LOG.debug("Duplicate definition on {} as {}", name, def, new Throwable());
309                 }
310             } else {
311                 throw resolveNamingConfict(existingDef, def, name);
312             }
313         }
314     }
315
316     private static IllegalStateException resolveNamingConfict(final SchemaNode existing, final SchemaNode incoming,
317             final JavaTypeName name) {
318         if (existing instanceof IdentitySchemaNode) {
319             if (incoming instanceof GroupingDefinition || incoming instanceof TypeDefinition
320                     || incoming instanceof DataSchemaNode || incoming instanceof NotificationDefinition
321                     || incoming instanceof OperationDefinition) {
322                 return new RenameMappingException(name, existing);
323             }
324         } else if (existing instanceof GroupingDefinition) {
325             if (incoming instanceof IdentitySchemaNode) {
326                 return new RenameMappingException(name, incoming);
327             }
328             if (incoming instanceof TypeDefinition || incoming instanceof DataSchemaNode
329                     || incoming instanceof NotificationDefinition || incoming instanceof OperationDefinition) {
330                 return new RenameMappingException(name, existing);
331             }
332         } else if (existing instanceof TypeDefinition) {
333             if (incoming instanceof GroupingDefinition || incoming instanceof IdentitySchemaNode) {
334                 return new RenameMappingException(name, incoming);
335             }
336             if (incoming instanceof DataSchemaNode || incoming instanceof NotificationDefinition
337                     || incoming instanceof OperationDefinition) {
338                 return new RenameMappingException(name, existing);
339             }
340         } else {
341             if (incoming instanceof GroupingDefinition || incoming instanceof IdentitySchemaNode
342                     || incoming instanceof TypeDefinition) {
343                 return new RenameMappingException(name, incoming);
344             }
345         }
346
347         return new IllegalStateException(String.format("Unhandled GeneratedType conflict between %s and %s on %s",
348             incoming, existing, name));
349     }
350 }