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