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