Added Support for Union Type def resolving and bug fixes.
[controller.git] / opendaylight / sal / yang-prototype / code-generator / binding-generator-impl / src / main / java / org / opendaylight / controller / sal / binding / yang / types / TypeProviderImpl.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.controller.sal.binding.yang.types;
9
10 import org.opendaylight.controller.binding.generator.util.ReferencedTypeImpl;
11 import org.opendaylight.controller.binding.generator.util.Types;
12 import org.opendaylight.controller.binding.generator.util.generated.type.builder.EnumerationBuilderImpl;
13 import org.opendaylight.controller.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
14 import org.opendaylight.controller.sal.binding.generator.spi.TypeProvider;
15 import org.opendaylight.controller.sal.binding.model.api.Enumeration;
16 import org.opendaylight.controller.sal.binding.model.api.GeneratedTransferObject;
17 import org.opendaylight.controller.sal.binding.model.api.Type;
18 import org.opendaylight.controller.sal.binding.model.api.type.builder.EnumBuilder;
19 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedPropertyBuilder;
20 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedTOBuilder;
21 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
22 import org.opendaylight.controller.yang.model.api.*;
23 import org.opendaylight.controller.yang.model.api.type.EnumTypeDefinition;
24 import org.opendaylight.controller.yang.model.api.type.EnumTypeDefinition.EnumPair;
25 import org.opendaylight.controller.yang.model.api.type.IdentityrefTypeDefinition;
26 import org.opendaylight.controller.yang.model.api.type.LeafrefTypeDefinition;
27 import org.opendaylight.controller.yang.model.api.type.UnionTypeDefinition;
28 import org.opendaylight.controller.yang.model.util.ExtendedType;
29
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import static org.opendaylight.controller.binding.generator.util.BindingGeneratorUtil.*;
36 import static org.opendaylight.controller.yang.model.util.SchemaContextUtil.*;
37
38 public final class TypeProviderImpl implements TypeProvider {
39
40     private final SchemaContext schemaContext;
41     private Map<String, Map<String, Type>> genTypeDefsContextMap;
42     private final Map<SchemaPath, Type> referencedTypes;
43
44     public TypeProviderImpl(final SchemaContext schemaContext) {
45         if (schemaContext == null) {
46             throw new IllegalArgumentException("Schema Context cannot be null!");
47         }
48
49         this.schemaContext = schemaContext;
50         this.genTypeDefsContextMap = new HashMap<>();
51         this.referencedTypes = new HashMap<>();
52         resolveTypeDefsFromContext();
53     }
54
55     public void putReferencedType(final SchemaPath refTypePath,
56                                   final Type refType) {
57         if (refTypePath == null) {
58             throw new IllegalArgumentException("Path reference of " +
59                     "Enumeration Type Definition cannot be NULL!");
60         }
61
62         if (refType == null) {
63             throw new IllegalArgumentException("Reference to Enumeration " +
64                     "Type cannot be NULL!");
65         }
66         referencedTypes.put(refTypePath, refType);
67     }
68
69     /*
70      * (non-Javadoc)
71      *
72      * @see org.opendaylight.controller.yang.model.type.provider.TypeProvider#
73      * javaTypeForYangType(java.lang.String)
74      */
75     @Override
76     public Type javaTypeForYangType(String type) {
77         Type t = BaseYangTypes.BASE_YANG_TYPES_PROVIDER
78                 .javaTypeForYangType(type);
79         return t;
80     }
81
82     @Override
83     public Type javaTypeForSchemaDefinitionType(
84             final TypeDefinition<?> typeDefinition) {
85         Type returnType = null;
86         if (typeDefinition == null) {
87             throw new IllegalArgumentException("Type Definition cannot be " +
88                     "NULL!");
89         }
90
91         if (typeDefinition.getQName() == null) {
92             throw new IllegalArgumentException("Type Definition cannot have " +
93                     "non specified QName (QName cannot be NULL!)");
94         }
95
96         if (typeDefinition.getQName().getLocalName() == null) {
97             throw new IllegalArgumentException("Type Definitions Local Name " +
98                     "cannot be NULL!");
99         }
100         final String typedefName = typeDefinition.getQName().getLocalName();
101         if (typeDefinition instanceof ExtendedType) {
102             final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
103
104             if (baseTypeDef instanceof LeafrefTypeDefinition) {
105                 final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) baseTypeDef;
106                 returnType = provideTypeForLeafref(leafref);
107             } else if (baseTypeDef instanceof IdentityrefTypeDefinition) {
108
109             } else if (baseTypeDef instanceof EnumTypeDefinition) {
110                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypeDef;
111                 returnType = resolveEnumFromTypeDefinition(enumTypeDef,
112                         typedefName);
113             } else {
114                 final Module module = findParentModuleForTypeDefinition(schemaContext,
115                         typeDefinition);
116                 if (module != null) {
117                     final Map<String, Type> genTOs = genTypeDefsContextMap
118                             .get(module.getName());
119                     if (genTOs != null) {
120                         returnType = genTOs.get(typedefName);
121                     }
122                     if (returnType == null) {
123                         returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER
124                                 .javaTypeForSchemaDefinitionType(baseTypeDef);
125                     }
126                 }
127             }
128         } else {
129             if (typeDefinition instanceof LeafrefTypeDefinition) {
130                 final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) typeDefinition;
131                 returnType = provideTypeForLeafref(leafref);
132             } else if (typeDefinition instanceof IdentityrefTypeDefinition) {
133
134             } else {
135                 returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER
136                         .javaTypeForSchemaDefinitionType(typeDefinition);
137             }
138         }
139         return returnType;
140     }
141
142     public Type generatedTypeForExtendedDefinitionType(
143             final TypeDefinition<?> typeDefinition) {
144         Type returnType = null;
145         if (typeDefinition == null) {
146             throw new IllegalArgumentException("Type Definition cannot be " +
147                     "NULL!");
148         }
149         if (typeDefinition.getQName() == null) {
150             throw new IllegalArgumentException("Type Definition cannot have " +
151                     "non specified QName (QName cannot be NULL!)");
152         }
153         if (typeDefinition.getQName() == null) {
154             throw new IllegalArgumentException("Type Definitions Local Name " +
155                     "cannot be NULL!");
156         }
157
158         final String typedefName = typeDefinition.getQName().getLocalName();
159         if (typeDefinition instanceof ExtendedType) {
160             final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
161
162             if (!(baseTypeDef instanceof LeafrefTypeDefinition)
163                     && !(baseTypeDef instanceof IdentityrefTypeDefinition)) {
164                 final Module module = findParentModuleForTypeDefinition(schemaContext,
165                         typeDefinition);
166
167                 if (module != null) {
168                     final Map<String, Type> genTOs = genTypeDefsContextMap
169                             .get(module.getName());
170                     if (genTOs != null) {
171                         returnType = genTOs.get(typedefName);
172                     }
173                 }
174             }
175         }
176         return returnType;
177     }
178
179     private TypeDefinition<?> baseTypeDefForExtendedType(
180             final TypeDefinition<?> extendTypeDef) {
181         if (extendTypeDef == null) {
182             throw new IllegalArgumentException("Type Definiition reference " +
183                     "cannot be NULL!");
184         }
185         final TypeDefinition<?> baseTypeDef = extendTypeDef.getBaseType();
186         if (baseTypeDef instanceof ExtendedType) {
187             return baseTypeDefForExtendedType(baseTypeDef);
188         } else {
189             return baseTypeDef;
190         }
191
192     }
193
194     public Type provideTypeForLeafref(final LeafrefTypeDefinition leafrefType) {
195         Type returnType = null;
196         if (leafrefType == null) {
197             throw new IllegalArgumentException("Leafref Type Definition " +
198                     "reference cannot be NULL!");
199         }
200
201         if (leafrefType.getPathStatement() == null) {
202             throw new IllegalArgumentException("The Path Statement for " +
203                     "Leafref Type Definition cannot be NULL!");
204         }
205
206         final RevisionAwareXPath xpath = leafrefType.getPathStatement();
207         final String strXPath = xpath.toString();
208
209         if (strXPath != null) {
210             if (strXPath.matches(".*//[.* | .*//].*")) {
211                 returnType = Types.typeForClass(Object.class);
212             } else {
213                 final Module module = findParentModuleForTypeDefinition(schemaContext, leafrefType);
214                 if (module != null) {
215                     final DataSchemaNode dataNode;
216                     if (xpath.isAbsolute()) {
217                         dataNode = findDataSchemaNode(schemaContext, module,
218                                 xpath);
219                     } else {
220                         dataNode = findDataSchemaNodeForRelativeXPath(schemaContext,
221                                 module, leafrefType, xpath);
222                     }
223
224                     if (leafContainsEnumDefinition(dataNode)) {
225                         returnType = referencedTypes.get(dataNode.getPath());
226                     } else if (leafListContainsEnumDefinition(dataNode)) {
227                         returnType = Types.listTypeFor(referencedTypes.get(
228                                 dataNode.getPath()));
229                     } else {
230                         returnType = resolveTypeFromDataSchemaNode(dataNode);
231                     }
232                 }
233             }
234         }
235         return returnType;
236     }
237
238     private boolean leafContainsEnumDefinition(final DataSchemaNode dataNode) {
239         if (dataNode instanceof LeafSchemaNode) {
240             final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
241             if (leaf.getType() instanceof EnumTypeDefinition) {
242                 return true;
243             }
244         }
245         return false;
246     }
247
248     private boolean leafListContainsEnumDefinition(
249             final DataSchemaNode dataNode) {
250         if (dataNode instanceof LeafListSchemaNode) {
251             final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
252             if (leafList.getType() instanceof EnumTypeDefinition) {
253                 return true;
254             }
255         }
256         return false;
257     }
258
259     private Enumeration resolveEnumFromTypeDefinition(
260             final EnumTypeDefinition enumTypeDef, final String enumName) {
261         if (enumTypeDef == null) {
262             throw new IllegalArgumentException("EnumTypeDefinition reference " +
263                     "cannot be NULL!");
264         }
265         if (enumTypeDef.getValues() == null) {
266             throw new IllegalArgumentException("EnumTypeDefinition MUST " +
267                     "contain at least ONE value definition!");
268         }
269         if (enumTypeDef.getQName() == null) {
270             throw new IllegalArgumentException("EnumTypeDefinition MUST " +
271                     "contain NON-NULL QName!");
272         }
273         if (enumTypeDef.getQName().getLocalName() == null) {
274             throw new IllegalArgumentException("Local Name in " +
275                     "EnumTypeDefinition QName cannot be NULL!");
276         }
277
278         final String enumerationName = parseToClassName(enumName);
279
280         Module module = findParentModuleForTypeDefinition(schemaContext, enumTypeDef);
281         final String basePackageName = moduleNamespaceToPackageName(module);
282
283         final EnumBuilder enumBuilder = new EnumerationBuilderImpl(
284                 basePackageName, enumerationName);
285         updateEnumPairsFromEnumTypeDef(enumTypeDef, enumBuilder);
286         return enumBuilder.toInstance(null);
287     }
288
289     private EnumBuilder resolveInnerEnumFromTypeDefinition(
290             final EnumTypeDefinition enumTypeDef, final String enumName,
291             final GeneratedTypeBuilder typeBuilder) {
292         if (enumTypeDef == null) {
293             throw new IllegalArgumentException("EnumTypeDefinition reference " +
294                     "cannot be NULL!");
295         }
296         if (enumTypeDef.getValues() == null) {
297             throw new IllegalArgumentException("EnumTypeDefinition MUST " +
298                     "contain at least ONE value definition!");
299         }
300         if (enumTypeDef.getQName() == null) {
301             throw new IllegalArgumentException("EnumTypeDefinition MUST " +
302                     "contain NON-NULL QName!");
303         }
304         if (enumTypeDef.getQName().getLocalName() == null) {
305             throw new IllegalArgumentException("Local Name in " +
306                     "EnumTypeDefinition QName cannot be NULL!");
307         }
308         if (typeBuilder == null) {
309             throw new IllegalArgumentException("Generated Type Builder " +
310                     "reference cannot be NULL!");
311         }
312
313         final String enumerationName = parseToClassName(enumName);
314         final EnumBuilder enumBuilder = typeBuilder
315                 .addEnumeration(enumerationName);
316
317         updateEnumPairsFromEnumTypeDef(enumTypeDef, enumBuilder);
318
319         return enumBuilder;
320     }
321
322     private void updateEnumPairsFromEnumTypeDef(
323             final EnumTypeDefinition enumTypeDef,
324             final EnumBuilder enumBuilder) {
325         if (enumBuilder != null) {
326             final List<EnumPair> enums = enumTypeDef.getValues();
327             if (enums != null) {
328                 int listIndex = 0;
329                 for (final EnumPair enumPair : enums) {
330                     if (enumPair != null) {
331                         final String enumPairName = parseToClassName(enumPair
332                                 .getName());
333                         Integer enumPairValue = enumPair.getValue();
334
335                         if (enumPairValue == null) {
336                             enumPairValue = listIndex;
337                         }
338                         enumBuilder.addValue(enumPairName, enumPairValue);
339                         listIndex++;
340                     }
341                 }
342             }
343         }
344     }
345
346     private Type resolveTypeFromDataSchemaNode(final DataSchemaNode dataNode) {
347         Type returnType = null;
348         if (dataNode != null) {
349             if (dataNode instanceof LeafSchemaNode) {
350                 final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
351                 returnType = javaTypeForSchemaDefinitionType(leaf.getType());
352             } else if (dataNode instanceof LeafListSchemaNode) {
353                 final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
354                 returnType = javaTypeForSchemaDefinitionType(leafList.getType());
355             }
356         }
357         return returnType;
358     }
359
360     private void resolveTypeDefsFromContext() {
361         final Set<Module> modules = schemaContext.getModules();
362         if (modules == null) {
363             throw new IllegalArgumentException("Sef of Modules cannot be " +
364                     "NULL!");
365         }
366         for (final Module module : modules) {
367             if (module == null) {
368                 continue;
369             }
370             final String moduleName = module.getName();
371             final String basePackageName = moduleNamespaceToPackageName(module);
372
373             final Set<TypeDefinition<?>> typeDefinitions = module
374                     .getTypeDefinitions();
375
376             final Map<String, Type> typeMap = new HashMap<>();
377             genTypeDefsContextMap.put(moduleName, typeMap);
378
379             if ((typeDefinitions != null) && (basePackageName != null)) {
380                 for (final TypeDefinition<?> typedef : typeDefinitions) {
381                     typedefToGeneratedType(basePackageName, moduleName, typedef);
382                 }
383                 final List<ExtendedType> extUnions = UnionDependencySort
384                         .sort(typeDefinitions);
385                 for (final ExtendedType extUnionType : extUnions) {
386                     addUnionGeneratedTypeDefinition(basePackageName, extUnionType);
387                 }
388             }
389         }
390     }
391
392     private Type typedefToGeneratedType(final String basePackageName,
393                                         final String moduleName, final TypeDefinition<?> typedef) {
394         if ((basePackageName != null) && (moduleName != null)
395                 && (typedef != null) && (typedef.getQName() != null)) {
396
397
398             final String typedefName = typedef.getQName().getLocalName();
399             final TypeDefinition<?> baseTypeDefinition = baseTypeDefForExtendedType(typedef);
400             if (!(baseTypeDefinition instanceof LeafrefTypeDefinition)
401                     && !(baseTypeDefinition instanceof IdentityrefTypeDefinition)) {
402                 Type returnType;
403                 if (baseTypeDefinition instanceof EnumTypeDefinition) {
404                     final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypeDefinition;
405                     returnType = resolveEnumFromTypeDefinition(enumTypeDef,
406                             typedefName);
407
408                 } else {
409                     final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER
410                             .javaTypeForSchemaDefinitionType(baseTypeDefinition);
411
412                     returnType = wrapJavaTypeIntoTO(basePackageName, typedef,
413                             javaType);
414                 }
415                 if (returnType != null) {
416                     final Map<String, Type> typeMap = genTypeDefsContextMap.get
417                             (moduleName);
418                     if (typeMap != null) {
419                         typeMap.put(typedefName, returnType);
420                     }
421                     return returnType;
422                 }
423             }
424         }
425         return null;
426     }
427
428     private GeneratedTransferObject wrapJavaTypeIntoTO(
429             final String basePackageName, final TypeDefinition<?> typedef,
430             final Type javaType) {
431         if (javaType != null) {
432             final String typedefName = typedef.getQName().getLocalName();
433             final String propertyName = parseToValidParamName(typedefName);
434
435             final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(
436                     basePackageName, typedef);
437
438             final GeneratedPropertyBuilder genPropBuilder = genTOBuilder
439                     .addProperty(propertyName);
440
441             genPropBuilder.addReturnType(javaType);
442             genTOBuilder.addEqualsIdentity(genPropBuilder);
443             genTOBuilder.addHashIdentity(genPropBuilder);
444             genTOBuilder.addToStringProperty(genPropBuilder);
445             return genTOBuilder.toInstance();
446         }
447         return null;
448     }
449
450     private void addUnionGeneratedTypeDefinition(final String basePackageName,
451                                                  final TypeDefinition<?> typedef) {
452         if (basePackageName == null) {
453             throw new IllegalArgumentException("Base Package Name cannot be " +
454                     "NULL!");
455         }
456         if (typedef == null) {
457             throw new IllegalArgumentException("Type Definition cannot be " +
458                     "NULL!");
459         }
460         if (typedef.getQName() == null) {
461             throw new IllegalArgumentException("Type Definition cannot have " +
462                     "non specified QName (QName cannot be NULL!)");
463         }
464
465         final TypeDefinition<?> baseTypeDefinition = typedef.getBaseType();
466         if ((baseTypeDefinition != null)
467                 && (baseTypeDefinition instanceof UnionTypeDefinition)) {
468             final UnionTypeDefinition unionTypeDef = (UnionTypeDefinition) baseTypeDefinition;
469             final List<TypeDefinition<?>> unionTypes = unionTypeDef
470                     .getTypes();
471             final Module parentModule = findParentModuleForTypeDefinition(schemaContext,
472                     typedef);
473
474             Map<String, Type> genTOsMap = null;
475             if (parentModule != null && parentModule.getName() != null) {
476                 genTOsMap = genTypeDefsContextMap.get(parentModule.getName());
477             }
478
479             final GeneratedTOBuilder unionGenTransObject = typedefToTransferObject(
480                     basePackageName, typedef);
481             if ((unionTypes != null) && (unionGenTransObject != null)) {
482                 for (final TypeDefinition<?> unionType : unionTypes) {
483                     final String typeName = unionType.getQName()
484                             .getLocalName();
485                     if (unionType instanceof ExtendedType) {
486                         final Module unionTypeModule = findParentModuleForTypeDefinition(schemaContext,
487                                 unionType);
488                         if (unionTypeModule != null && unionTypeModule.getName() != null) {
489                             final Map<String, Type> innerGenTOs = genTypeDefsContextMap
490                                     .get(unionTypeModule.getName());
491
492                             final GeneratedTransferObject genTransferObject =
493                                     (GeneratedTransferObject) innerGenTOs.get(typeName);
494                             if (genTransferObject != null) {
495                                 updateUnionTypeAsProperty(unionGenTransObject,
496                                         genTransferObject,
497                                         genTransferObject.getName());
498                             }
499                         }
500                     } else if (unionType instanceof EnumTypeDefinition) {
501                         final EnumBuilder
502                                 enumBuilder = resolveInnerEnumFromTypeDefinition(
503                                 (EnumTypeDefinition) unionType, typeName,
504                                 unionGenTransObject);
505                         final Type enumRefType = new ReferencedTypeImpl(
506                                 enumBuilder.getPackageName(),
507                                 enumBuilder.getName());
508                         updateUnionTypeAsProperty(unionGenTransObject,
509                                 enumRefType, typeName);
510                     } else {
511                         final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER
512                                 .javaTypeForSchemaDefinitionType(unionType);
513                         if (javaType != null) {
514                             updateUnionTypeAsProperty(unionGenTransObject,
515                                     javaType, typeName);
516                         }
517                     }
518                 }
519                 genTOsMap.put(typedef.getQName().getLocalName(),
520                         unionGenTransObject.toInstance());
521             }
522         }
523     }
524
525     private void updateUnionTypeAsProperty(
526             final GeneratedTOBuilder unionGenTransObject, final Type type,
527             final String propertyName) {
528         if (unionGenTransObject != null && type != null) {
529             final GeneratedPropertyBuilder propBuilder =
530                     unionGenTransObject.addProperty(parseToValidParamName(
531                             propertyName));
532             propBuilder.addReturnType(type);
533             propBuilder.setReadOnly(false);
534
535             if (!(type instanceof Enumeration)) {
536                 unionGenTransObject.addEqualsIdentity(propBuilder);
537                 unionGenTransObject.addHashIdentity(propBuilder);
538                 unionGenTransObject.addToStringProperty(propBuilder);
539             }
540         }
541     }
542
543     private GeneratedTOBuilder typedefToTransferObject(
544             final String basePackageName, final TypeDefinition<?> typedef) {
545
546         final String packageName = packageNameForGeneratedType(basePackageName,
547                 typedef.getPath());
548         final String typeDefTOName = typedef.getQName().getLocalName();
549
550         if ((packageName != null) && (typedef != null)
551                 && (typeDefTOName != null)) {
552             final String genTOName = parseToClassName(typeDefTOName);
553             final GeneratedTOBuilder newType = new GeneratedTOBuilderImpl(
554                     packageName, genTOName);
555
556             return newType;
557         }
558         return null;
559     }
560 }