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