Merge "Fix minor bug in FRM proactive flow code path"
[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 static org.opendaylight.controller.binding.generator.util.BindingGeneratorUtil.*;
11 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.*;
12
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.TreeMap;
19
20 import org.apache.commons.lang.StringEscapeUtils;
21 import org.opendaylight.controller.binding.generator.util.ReferencedTypeImpl;
22 import org.opendaylight.controller.binding.generator.util.TypeConstants;
23 import org.opendaylight.controller.binding.generator.util.Types;
24 import org.opendaylight.controller.binding.generator.util.generated.type.builder.EnumerationBuilderImpl;
25 import org.opendaylight.controller.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
26 import org.opendaylight.controller.sal.binding.generator.spi.TypeProvider;
27 import org.opendaylight.controller.sal.binding.model.api.Enumeration;
28 import org.opendaylight.controller.sal.binding.model.api.GeneratedTransferObject;
29 import org.opendaylight.controller.sal.binding.model.api.Type;
30 import org.opendaylight.controller.sal.binding.model.api.type.builder.EnumBuilder;
31 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedPropertyBuilder;
32 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedTOBuilder;
33 import org.opendaylight.controller.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.Module;
40 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
44 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
45 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
46 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
47 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
48 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
49 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
50 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
51 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
52 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
53 import org.opendaylight.yangtools.yang.model.util.StringType;
54 import org.opendaylight.yangtools.yang.model.util.UnionType;
55
56 public final class TypeProviderImpl implements TypeProvider {
57
58     private final SchemaContext schemaContext;
59     private Map<String, Map<String, Type>> genTypeDefsContextMap;
60     private final Map<SchemaPath, Type> referencedTypes;
61
62     public TypeProviderImpl(final SchemaContext schemaContext) {
63         if (schemaContext == null) {
64             throw new IllegalArgumentException("Schema Context cannot be null!");
65         }
66
67         this.schemaContext = schemaContext;
68         this.genTypeDefsContextMap = new HashMap<>();
69         this.referencedTypes = new HashMap<>();
70         resolveTypeDefsFromContext();
71     }
72
73     public void putReferencedType(final SchemaPath refTypePath, final Type refType) {
74         if (refTypePath == null) {
75             throw new IllegalArgumentException("Path reference of " + "Enumeration Type Definition cannot be NULL!");
76         }
77
78         if (refType == null) {
79             throw new IllegalArgumentException("Reference to Enumeration " + "Type cannot be NULL!");
80         }
81         referencedTypes.put(refTypePath, refType);
82     }
83
84     /*
85      * (non-Javadoc)
86      *
87      * @see org.opendaylight.controller.yang.model.type.provider.TypeProvider#
88      * javaTypeForYangType(java.lang.String)
89      */
90     @Override
91     public Type javaTypeForYangType(String type) {
92         Type t = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForYangType(type);
93         return t;
94     }
95
96     @Override
97     public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition) {
98         Type returnType = null;
99         if (typeDefinition == null) {
100             throw new IllegalArgumentException("Type Definition cannot be NULL!");
101         }
102         if (typeDefinition.getQName() == null) {
103             throw new IllegalArgumentException(
104                     "Type Definition cannot have non specified QName (QName cannot be NULL!)");
105         }
106         if (typeDefinition.getQName().getLocalName() == null) {
107             throw new IllegalArgumentException("Type Definitions Local Name cannot be NULL!");
108         }
109         final String typedefName = typeDefinition.getQName().getLocalName();
110         if (typeDefinition instanceof ExtendedType) {
111             final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
112
113             if (baseTypeDef instanceof LeafrefTypeDefinition) {
114                 final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) baseTypeDef;
115                 returnType = provideTypeForLeafref(leafref);
116             } else if (baseTypeDef instanceof IdentityrefTypeDefinition) {
117                 final IdentityrefTypeDefinition idref = (IdentityrefTypeDefinition) baseTypeDef;
118                 returnType = returnTypeForIdentityref(idref);
119             } else if (baseTypeDef instanceof EnumTypeDefinition) {
120                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypeDef;
121                 returnType = resolveEnumFromTypeDefinition(enumTypeDef, typedefName);
122             } else {
123                 final Module module = findParentModuleForTypeDefinition(schemaContext, typeDefinition);
124                 if (module != null) {
125                     final Map<String, Type> genTOs = genTypeDefsContextMap.get(module.getName());
126                     if (genTOs != null) {
127                         returnType = genTOs.get(typedefName);
128                     }
129                     if (returnType == null) {
130                         returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER
131                                 .javaTypeForSchemaDefinitionType(baseTypeDef);
132                     }
133                 }
134             }
135         } else {
136             if (typeDefinition instanceof LeafrefTypeDefinition) {
137                 final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) typeDefinition;
138                 returnType = provideTypeForLeafref(leafref);
139             } else if (typeDefinition instanceof IdentityrefTypeDefinition) {
140                 final IdentityrefTypeDefinition idref = (IdentityrefTypeDefinition) typeDefinition;
141                 returnType = returnTypeForIdentityref(idref);
142             } else {
143                 returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(typeDefinition);
144             }
145         }
146         // TODO: add throw exception when we will be able to resolve ALL yang
147         // types!
148         // if (returnType == null) {
149         // throw new IllegalArgumentException("Type Provider can't resolve " +
150         // "type for specified Type Definition " + typedefName);
151         // }
152         return returnType;
153     }
154
155     private Type returnTypeForIdentityref(IdentityrefTypeDefinition idref) {
156         QName baseIdQName = idref.getIdentity();
157         Module module = schemaContext.findModuleByNamespace(baseIdQName.getNamespace());
158         IdentitySchemaNode identity = null;
159         for (IdentitySchemaNode id : module.getIdentities()) {
160             if (id.getQName().equals(baseIdQName)) {
161                 identity = id;
162             }
163         }
164         if (identity == null) {
165             throw new IllegalArgumentException("Target identity '" + baseIdQName + "' do not exists");
166         }
167
168         final String basePackageName = moduleNamespaceToPackageName(module);
169         final String packageName = packageNameForGeneratedType(basePackageName, identity.getPath());
170         final String genTypeName = parseToClassName(identity.getQName().getLocalName());
171
172         Type baseType = Types.typeForClass(Class.class);
173         Type paramType = Types.wildcardTypeFor(packageName, genTypeName);
174         Type returnType = Types.parameterizedTypeFor(baseType, paramType);
175         return returnType;
176     }
177
178     public Type generatedTypeForExtendedDefinitionType(final TypeDefinition<?> typeDefinition) {
179         Type returnType = null;
180         if (typeDefinition == null) {
181             throw new IllegalArgumentException("Type Definition cannot be NULL!");
182         }
183         if (typeDefinition.getQName() == null) {
184             throw new IllegalArgumentException(
185                     "Type Definition cannot have non specified QName (QName cannot be NULL!)");
186         }
187         if (typeDefinition.getQName() == null) {
188             throw new IllegalArgumentException("Type Definitions Local Name cannot be NULL!");
189         }
190
191         final String typedefName = typeDefinition.getQName().getLocalName();
192         if (typeDefinition instanceof ExtendedType) {
193             final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
194
195             if (!(baseTypeDef instanceof LeafrefTypeDefinition) && !(baseTypeDef instanceof IdentityrefTypeDefinition)) {
196                 final Module module = findParentModuleForTypeDefinition(schemaContext, typeDefinition);
197
198                 if (module != null) {
199                     final Map<String, Type> genTOs = genTypeDefsContextMap.get(module.getName());
200                     if (genTOs != null) {
201                         returnType = genTOs.get(typedefName);
202                     }
203                 }
204             }
205         }
206         return returnType;
207     }
208
209     private TypeDefinition<?> baseTypeDefForExtendedType(final TypeDefinition<?> extendTypeDef) {
210         if (extendTypeDef == null) {
211             throw new IllegalArgumentException("Type Definiition reference cannot be NULL!");
212         }
213         final TypeDefinition<?> baseTypeDef = extendTypeDef.getBaseType();
214         if (baseTypeDef instanceof ExtendedType) {
215             return baseTypeDefForExtendedType(baseTypeDef);
216         } else {
217             return baseTypeDef;
218         }
219
220     }
221
222     public Type provideTypeForLeafref(final LeafrefTypeDefinition leafrefType) {
223         Type returnType = null;
224         if (leafrefType == null) {
225             throw new IllegalArgumentException("Leafref Type Definition reference cannot be NULL!");
226         }
227
228         if (leafrefType.getPathStatement() == null) {
229             throw new IllegalArgumentException("The Path Statement for Leafref Type Definition cannot be NULL!");
230         }
231
232         final RevisionAwareXPath xpath = leafrefType.getPathStatement();
233         final String strXPath = xpath.toString();
234
235         if (strXPath != null) {
236             if (strXPath.contains("[")) {
237                 returnType = Types.typeForClass(Object.class);
238             } else {
239                 final Module module = findParentModuleForTypeDefinition(schemaContext, leafrefType);
240                 if (module != null) {
241                     final DataSchemaNode dataNode;
242                     if (xpath.isAbsolute()) {
243                         dataNode = findDataSchemaNode(schemaContext, module, xpath);
244                     } else {
245                         dataNode = findDataSchemaNodeForRelativeXPath(schemaContext, module, leafrefType, xpath);
246                     }
247
248                     if (leafContainsEnumDefinition(dataNode)) {
249                         returnType = referencedTypes.get(dataNode.getPath());
250                     } else if (leafListContainsEnumDefinition(dataNode)) {
251                         returnType = Types.listTypeFor(referencedTypes.get(dataNode.getPath()));
252                     } else {
253                         returnType = resolveTypeFromDataSchemaNode(dataNode);
254                     }
255                 }
256             }
257         }
258         return returnType;
259     }
260
261     private boolean leafContainsEnumDefinition(final DataSchemaNode dataNode) {
262         if (dataNode instanceof LeafSchemaNode) {
263             final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
264             if (leaf.getType() instanceof EnumTypeDefinition) {
265                 return true;
266             }
267         }
268         return false;
269     }
270
271     private boolean leafListContainsEnumDefinition(final DataSchemaNode dataNode) {
272         if (dataNode instanceof LeafListSchemaNode) {
273             final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
274             if (leafList.getType() instanceof EnumTypeDefinition) {
275                 return true;
276             }
277         }
278         return false;
279     }
280
281     private Enumeration resolveEnumFromTypeDefinition(final EnumTypeDefinition enumTypeDef, final String enumName) {
282         if (enumTypeDef == null) {
283             throw new IllegalArgumentException("EnumTypeDefinition reference cannot be NULL!");
284         }
285         if (enumTypeDef.getValues() == null) {
286             throw new IllegalArgumentException("EnumTypeDefinition MUST contain at least ONE value definition!");
287         }
288         if (enumTypeDef.getQName() == null) {
289             throw new IllegalArgumentException("EnumTypeDefinition MUST contain NON-NULL QName!");
290         }
291         if (enumTypeDef.getQName().getLocalName() == null) {
292             throw new IllegalArgumentException("Local Name in EnumTypeDefinition QName cannot be NULL!");
293         }
294
295         final String enumerationName = parseToClassName(enumName);
296
297         Module module = findParentModuleForTypeDefinition(schemaContext, enumTypeDef);
298         final String basePackageName = moduleNamespaceToPackageName(module);
299
300         final EnumBuilder enumBuilder = new EnumerationBuilderImpl(basePackageName, enumerationName);
301         updateEnumPairsFromEnumTypeDef(enumTypeDef, enumBuilder);
302         return enumBuilder.toInstance(null);
303     }
304
305     private EnumBuilder resolveInnerEnumFromTypeDefinition(final EnumTypeDefinition enumTypeDef, final String enumName,
306             final GeneratedTypeBuilder typeBuilder) {
307         if (enumTypeDef == null) {
308             throw new IllegalArgumentException("EnumTypeDefinition reference cannot be NULL!");
309         }
310         if (enumTypeDef.getValues() == null) {
311             throw new IllegalArgumentException("EnumTypeDefinition MUST contain at least ONE value definition!");
312         }
313         if (enumTypeDef.getQName() == null) {
314             throw new IllegalArgumentException("EnumTypeDefinition MUST contain NON-NULL QName!");
315         }
316         if (enumTypeDef.getQName().getLocalName() == null) {
317             throw new IllegalArgumentException("Local Name in EnumTypeDefinition QName cannot be NULL!");
318         }
319         if (typeBuilder == null) {
320             throw new IllegalArgumentException("Generated Type Builder reference cannot be NULL!");
321         }
322
323         final String enumerationName = parseToClassName(enumName);
324         final EnumBuilder enumBuilder = typeBuilder.addEnumeration(enumerationName);
325
326         updateEnumPairsFromEnumTypeDef(enumTypeDef, enumBuilder);
327
328         return enumBuilder;
329     }
330
331     private void updateEnumPairsFromEnumTypeDef(final EnumTypeDefinition enumTypeDef, final EnumBuilder enumBuilder) {
332         if (enumBuilder != null) {
333             final List<EnumPair> enums = enumTypeDef.getValues();
334             if (enums != null) {
335                 int listIndex = 0;
336                 for (final EnumPair enumPair : enums) {
337                     if (enumPair != null) {
338                         final String enumPairName = parseToClassName(enumPair.getName());
339                         Integer enumPairValue = enumPair.getValue();
340
341                         if (enumPairValue == null) {
342                             enumPairValue = listIndex;
343                         }
344                         enumBuilder.addValue(enumPairName, enumPairValue);
345                         listIndex++;
346                     }
347                 }
348             }
349         }
350     }
351
352     private Type resolveTypeFromDataSchemaNode(final DataSchemaNode dataNode) {
353         Type returnType = null;
354         if (dataNode != null) {
355             if (dataNode instanceof LeafSchemaNode) {
356                 final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
357                 returnType = javaTypeForSchemaDefinitionType(leaf.getType());
358             } else if (dataNode instanceof LeafListSchemaNode) {
359                 final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
360                 returnType = javaTypeForSchemaDefinitionType(leafList.getType());
361             }
362         }
363         return returnType;
364     }
365
366     private void resolveTypeDefsFromContext() {
367         final Set<Module> modules = schemaContext.getModules();
368         if (modules == null) {
369             throw new IllegalArgumentException("Sef of Modules cannot be NULL!");
370         }
371         for (final Module module : modules) {
372             if (module == null) {
373                 continue;
374             }
375             final String moduleName = module.getName();
376             final String basePackageName = moduleNamespaceToPackageName(module);
377
378             final Set<TypeDefinition<?>> typeDefinitions = module.getTypeDefinitions();
379             final List<TypeDefinition<?>> listTypeDefinitions = sortTypeDefinitionAccordingDepth(typeDefinitions);
380
381             final Map<String, Type> typeMap = new HashMap<>();
382             genTypeDefsContextMap.put(moduleName, typeMap);
383
384             if ((listTypeDefinitions != null) && (basePackageName != null)) {
385                 for (final TypeDefinition<?> typedef : listTypeDefinitions) {
386                     typedefToGeneratedType(basePackageName, moduleName, typedef);
387                 }
388             }
389         }
390     }
391
392     private Type typedefToGeneratedType(final String basePackageName, final String moduleName,
393             final TypeDefinition<?> typedef) {
394         if ((basePackageName != null) && (moduleName != null) && (typedef != null) && (typedef.getQName() != null)) {
395
396             final String typedefName = typedef.getQName().getLocalName();
397             final TypeDefinition<?> innerTypeDefinition = typedef.getBaseType();
398             if (!(innerTypeDefinition instanceof LeafrefTypeDefinition)
399                     && !(innerTypeDefinition instanceof IdentityrefTypeDefinition)) {
400                 Type returnType = null;
401                 if (innerTypeDefinition instanceof ExtendedType) {
402                     ExtendedType extendedTypeDef = (ExtendedType) innerTypeDefinition;
403                     returnType = resolveExtendedTypeFromTypeDef(extendedTypeDef, basePackageName, typedefName,
404                             moduleName);
405                 } else if (innerTypeDefinition instanceof UnionTypeDefinition) {
406                     final GeneratedTOBuilder genTOBuilder = addUnionGeneratedTypeDefinition(basePackageName, typedef,
407                             typedefName);
408                     returnType = genTOBuilder.toInstance();
409                 } else if (innerTypeDefinition instanceof EnumTypeDefinition) {
410                     final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) innerTypeDefinition;
411                     returnType = resolveEnumFromTypeDefinition(enumTypeDef, typedefName);
412
413                 } else if (innerTypeDefinition instanceof BitsTypeDefinition) {
414                     final BitsTypeDefinition bitsTypeDefinition = (BitsTypeDefinition) innerTypeDefinition;
415                     final GeneratedTOBuilder genTOBuilder = bitsTypedefToTransferObject(basePackageName,
416                             bitsTypeDefinition, typedefName);
417                     returnType = genTOBuilder.toInstance();
418
419                 } else {
420                     final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER
421                             .javaTypeForSchemaDefinitionType(innerTypeDefinition);
422
423                     returnType = wrapJavaTypeIntoTO(basePackageName, typedef, javaType);
424                 }
425                 if (returnType != null) {
426                     final Map<String, Type> typeMap = genTypeDefsContextMap.get(moduleName);
427                     if (typeMap != null) {
428                         typeMap.put(typedefName, returnType);
429                     }
430                     return returnType;
431                 }
432             }
433         }
434         return null;
435     }
436
437     private GeneratedTransferObject wrapJavaTypeIntoTO(final String basePackageName, final TypeDefinition<?> typedef,
438             final Type javaType) {
439         if (javaType != null) {
440             final String typedefName = typedef.getQName().getLocalName();
441             final String propertyName = parseToValidParamName(typedefName);
442
443             final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(basePackageName, typedef);
444
445             final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty(propertyName);
446
447             genPropBuilder.setReturnType(javaType);
448             genTOBuilder.addEqualsIdentity(genPropBuilder);
449             genTOBuilder.addHashIdentity(genPropBuilder);
450             genTOBuilder.addToStringProperty(genPropBuilder);
451             if (javaType == BaseYangTypes.STRING_TYPE) {
452                 if (typedef instanceof ExtendedType) {
453                     final List<String> regExps = resolveRegExpressionsFromTypedef((ExtendedType) typedef);
454                     addStringRegExAsConstant(genTOBuilder, regExps);
455                 }
456             }
457             return genTOBuilder.toInstance();
458         }
459         return null;
460     }
461
462     public GeneratedTOBuilder addUnionGeneratedTypeDefinition(final String basePackageName,
463             final TypeDefinition<?> typedef, String typeDefName) {
464         if (basePackageName == null) {
465             throw new IllegalArgumentException("Base Package Name cannot be NULL!");
466         }
467         if (typedef == null) {
468             throw new IllegalArgumentException("Type Definition cannot be NULL!");
469         }
470         if (typedef.getQName() == null) {
471             throw new IllegalArgumentException(
472                     "Type Definition cannot have non specified QName (QName cannot be NULL!)");
473         }
474
475         final TypeDefinition<?> baseTypeDefinition = typedef.getBaseType();
476         if ((baseTypeDefinition != null) && (baseTypeDefinition instanceof UnionTypeDefinition)) {
477             final Module parentModule = findParentModuleForTypeDefinition(schemaContext, typedef);
478             final UnionTypeDefinition unionTypeDef = (UnionTypeDefinition) baseTypeDefinition;
479             final List<TypeDefinition<?>> unionTypes = unionTypeDef.getTypes();
480
481             Map<String, Type> genTOsMap = null;
482             if (parentModule != null && parentModule.getName() != null) {
483                 genTOsMap = genTypeDefsContextMap.get(parentModule.getName());
484             }
485
486             final GeneratedTOBuilder unionGenTransObject;
487             if (typeDefName != null && !typeDefName.isEmpty()) {
488                 final String typeName = parseToClassName(typeDefName);
489                 unionGenTransObject = new GeneratedTOBuilderImpl(basePackageName, typeName);
490             } else {
491                 unionGenTransObject = typedefToTransferObject(basePackageName, typedef);
492             }
493             unionGenTransObject.setIsUnion(true);
494
495             final List<String> regularExpressions = new ArrayList<String>();
496             for (final TypeDefinition<?> unionType : unionTypes) {
497                 final String typeName = unionType.getQName().getLocalName();
498                 if (unionType instanceof ExtendedType) {
499                     final Module unionTypeModule = findParentModuleForTypeDefinition(schemaContext, unionType);
500                     if (unionTypeModule != null && unionTypeModule.getName() != null) {
501                         final Map<String, Type> innerGenTOs = genTypeDefsContextMap.get(unionTypeModule.getName());
502                         Type genTransferObject = null;
503                         if (innerGenTOs != null) {
504                             genTransferObject = innerGenTOs.get(typeName);
505                         }
506                         if (genTransferObject != null) {
507                             updateUnionTypeAsProperty(unionGenTransObject, genTransferObject,
508                                     genTransferObject.getName());
509                         } else {
510                             final TypeDefinition<?> baseType = baseTypeDefForExtendedType(unionType);
511                             if (typeName.equals(baseType.getQName().getLocalName())) {
512                                 final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER
513                                         .javaTypeForSchemaDefinitionType(baseType);
514                                 if (javaType != null) {
515                                     updateUnionTypeAsProperty(unionGenTransObject, javaType, typeName);
516                                 }
517                             }
518                             if (baseType instanceof StringType) {
519                                 regularExpressions.addAll(resolveRegExpressionsFromTypedef((ExtendedType) unionType));
520                             }
521                         }
522                     }
523                 } else if (unionType instanceof EnumTypeDefinition) {
524                     final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition((EnumTypeDefinition) unionType,
525                             typeName, unionGenTransObject);
526                     final Type enumRefType = new ReferencedTypeImpl(enumBuilder.getPackageName(), enumBuilder.getName());
527                     updateUnionTypeAsProperty(unionGenTransObject, enumRefType, typeName);
528                 } else {
529                     final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER
530                             .javaTypeForSchemaDefinitionType(unionType);
531                     if (javaType != null) {
532                         updateUnionTypeAsProperty(unionGenTransObject, javaType, typeName);
533                     }
534                 }
535             }
536             if (!regularExpressions.isEmpty()) {
537                 addStringRegExAsConstant(unionGenTransObject, regularExpressions);
538             }
539
540             genTOsMap.put(typedef.getQName().getLocalName(), unionGenTransObject.toInstance());
541             return unionGenTransObject;
542         }
543         return null;
544     }
545
546     private void updateUnionTypeAsProperty(final GeneratedTOBuilder unionGenTransObject, final Type type,
547             final String propertyName) {
548         if (unionGenTransObject != null && type != null) {
549             if (!unionGenTransObject.containsProperty(propertyName)) {
550                 final GeneratedPropertyBuilder propBuilder = unionGenTransObject
551                         .addProperty(parseToValidParamName(propertyName));
552                 propBuilder.setReturnType(type);
553
554                 if (!(type instanceof Enumeration)) {
555                     unionGenTransObject.addEqualsIdentity(propBuilder);
556                     unionGenTransObject.addHashIdentity(propBuilder);
557                     unionGenTransObject.addToStringProperty(propBuilder);
558                 }
559             }
560         }
561     }
562
563     private GeneratedTOBuilder typedefToTransferObject(final String basePackageName, final TypeDefinition<?> typedef) {
564
565         final String packageName = packageNameForGeneratedType(basePackageName, typedef.getPath());
566         final String typeDefTOName = typedef.getQName().getLocalName();
567
568         if ((packageName != null) && (typedef != null) && (typeDefTOName != null)) {
569             final String genTOName = parseToClassName(typeDefTOName);
570             final GeneratedTOBuilder newType = new GeneratedTOBuilderImpl(packageName, genTOName);
571
572             return newType;
573         }
574         return null;
575     }
576
577     public GeneratedTOBuilder bitsTypedefToTransferObject(final String basePackageName,
578             final TypeDefinition<?> typeDef, String typeDefName) {
579
580         if (typeDef == null) {
581             throw new IllegalArgumentException("typeDef cannot be NULL!");
582         }
583         if (basePackageName == null) {
584             throw new IllegalArgumentException("Base Package Name cannot be NULL!");
585         }
586
587         if (typeDef instanceof BitsTypeDefinition) {
588             BitsTypeDefinition bitsTypeDefinition = (BitsTypeDefinition) typeDef;
589
590             final String typeName = parseToClassName(typeDefName);
591             final GeneratedTOBuilder genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typeName);
592
593             final List<Bit> bitList = bitsTypeDefinition.getBits();
594             GeneratedPropertyBuilder genPropertyBuilder;
595             for (final Bit bit : bitList) {
596                 String name = bit.getName();
597                 genPropertyBuilder = genTOBuilder.addProperty(parseToValidParamName(name));
598                 genPropertyBuilder.setReadOnly(false);
599                 genPropertyBuilder.setReturnType(BaseYangTypes.BOOLEAN_TYPE);
600
601                 genTOBuilder.addEqualsIdentity(genPropertyBuilder);
602                 genTOBuilder.addHashIdentity(genPropertyBuilder);
603                 genTOBuilder.addToStringProperty(genPropertyBuilder);
604             }
605
606             return genTOBuilder;
607         }
608         return null;
609     }
610
611     private List<String> resolveRegExpressionsFromTypedef(ExtendedType typedef) {
612         final List<String> regExps = new ArrayList<String>();
613         if (typedef == null) {
614             throw new IllegalArgumentException("typedef can't be null");
615         }
616         final TypeDefinition<?> strTypeDef = baseTypeDefForExtendedType(typedef);
617         if (strTypeDef instanceof StringType) {
618             final List<PatternConstraint> patternConstraints = typedef.getPatterns();
619             if (!patternConstraints.isEmpty()) {
620                 String regEx;
621                 String modifiedRegEx;
622                 for (PatternConstraint patternConstraint : patternConstraints) {
623                     regEx = patternConstraint.getRegularExpression();
624                     modifiedRegEx = StringEscapeUtils.escapeJava(regEx);
625                     regExps.add(modifiedRegEx);
626                 }
627             }
628         }
629         return regExps;
630     }
631
632     private void addStringRegExAsConstant(GeneratedTOBuilder genTOBuilder, List<String> regularExpressions) {
633         if (genTOBuilder == null)
634             throw new IllegalArgumentException("genTOBuilder can't be null");
635         if (regularExpressions == null)
636             throw new IllegalArgumentException("regularExpressions can't be null");
637
638         if (!regularExpressions.isEmpty()) {
639             genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), TypeConstants.PATTERN_CONSTANT_NAME,
640                     regularExpressions);
641         }
642     }
643
644     private GeneratedTransferObject resolveExtendedTypeFromTypeDef(final ExtendedType extendedType,
645             final String basePackageName, final String typedefName, final String moduleName) {
646
647         if (extendedType == null) {
648             throw new IllegalArgumentException("Extended type cannot be NULL!");
649         }
650         if (basePackageName == null) {
651             throw new IllegalArgumentException("String with base package name cannot be NULL!");
652         }
653         if (typedefName == null) {
654             throw new IllegalArgumentException("String with type definition name cannot be NULL!");
655         }
656
657         final String typeDefName = parseToClassName(typedefName);
658         final String lowTypeDef = extendedType.getQName().getLocalName();
659         final GeneratedTOBuilder genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typeDefName);
660
661         final Map<String, Type> typeMap = genTypeDefsContextMap.get(moduleName);
662         if (typeMap != null) {
663             Type type = typeMap.get(lowTypeDef);
664             if (type instanceof GeneratedTransferObject) {
665                 genTOBuilder.setExtendsType((GeneratedTransferObject) type);
666             }
667         }
668
669         return genTOBuilder.toInstance();
670     }
671
672     /**
673      * The method find out for each type definition how many immersion (depth)
674      * is necessary to get to the base type. Every type definition is inserted
675      * to the map which key is depth and value is list of type definitions with
676      * equal depth. In next step are lists from this map concatenated to one
677      * list in ascending order according to their depth. All type definitions
678      * are in the list behind all type definitions on which depends.
679      *
680      * @param unsortedTypeDefinitions
681      *            represents list of type definitions
682      * @return list of type definitions sorted according their each other
683      *         dependencies (type definitions which are depend on other type
684      *         definitions are in list behind them).
685      */
686     private List<TypeDefinition<?>> sortTypeDefinitionAccordingDepth(
687             final Set<TypeDefinition<?>> unsortedTypeDefinitions) {
688         List<TypeDefinition<?>> sortedTypeDefinition = new ArrayList<>();
689
690         Map<Integer, List<TypeDefinition<?>>> typeDefinitionsDepths = new TreeMap<>();
691         for (TypeDefinition<?> unsortedTypeDefinition : unsortedTypeDefinitions) {
692             final int depth = getTypeDefinitionDepth(unsortedTypeDefinition);
693             List<TypeDefinition<?>> typeDefinitionsConcreteDepth = typeDefinitionsDepths.get(depth);
694             if (typeDefinitionsConcreteDepth == null) {
695                 typeDefinitionsConcreteDepth = new ArrayList<TypeDefinition<?>>();
696                 typeDefinitionsDepths.put(depth, typeDefinitionsConcreteDepth);
697             }
698             typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
699         }
700
701         Set<Integer> depths = typeDefinitionsDepths.keySet(); // keys are in
702                                                               // ascending order
703         for (Integer depth : depths) {
704             sortedTypeDefinition.addAll(typeDefinitionsDepths.get(depth));
705         }
706
707         return sortedTypeDefinition;
708     }
709
710     /**
711      * The method return how many immersion is necessary to get from type
712      * definition to base type.
713      *
714      * @param typeDefinition
715      *            is type definition for which is depth looked for.
716      * @return how many immersion is necessary to get from type definition to
717      *         base type
718      */
719     private int getTypeDefinitionDepth(final TypeDefinition<?> typeDefinition) {
720         if (typeDefinition == null) {
721             throw new IllegalArgumentException("Type definition can't be null");
722         }
723         int depth = 1;
724         TypeDefinition<?> baseType = typeDefinition.getBaseType();
725
726         if (baseType instanceof ExtendedType) {
727             depth = depth + getTypeDefinitionDepth(typeDefinition.getBaseType());
728         } else if (baseType instanceof UnionType) {
729             List<TypeDefinition<?>> childTypeDefinitions = ((UnionType) baseType).getTypes();
730             int maxChildDepth = 0;
731             int childDepth = 1;
732             for (TypeDefinition<?> childTypeDefinition : childTypeDefinitions) {
733                 childDepth = childDepth + getTypeDefinitionDepth(childTypeDefinition.getBaseType());
734                 if (childDepth > maxChildDepth) {
735                     maxChildDepth = childDepth;
736                 }
737             }
738             return maxChildDepth;
739         }
740         return depth;
741     }
742
743 }