712d079c32fd60b74f1375533a606e05085f7b9d
[yangtools.git] / code-generator / binding-type-provider / 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.Collection;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.TreeMap;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22
23 import org.apache.commons.lang3.StringEscapeUtils;
24 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
25 import org.opendaylight.yangtools.binding.generator.util.TypeConstants;
26 import org.opendaylight.yangtools.binding.generator.util.Types;
27 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.EnumerationBuilderImpl;
28 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
29 import org.opendaylight.yangtools.sal.binding.generator.spi.TypeProvider;
30 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
31 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration;
32 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject;
33 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions;
34 import org.opendaylight.yangtools.sal.binding.model.api.Type;
35 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.EnumBuilder;
36 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedPropertyBuilder;
37 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
38 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.Module;
44 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
48 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
49 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
50 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
51 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
52 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
53 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
54 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
55 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
56 import org.opendaylight.yangtools.yang.model.util.DataNodeIterator;
57 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
58 import org.opendaylight.yangtools.yang.model.util.StringType;
59 import org.opendaylight.yangtools.yang.model.util.UnionType;
60 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
61
62 import com.google.common.base.Preconditions;
63
64 public final class TypeProviderImpl implements TypeProvider {
65     /**
66      * Contains the schema data red from YANG files.
67      */
68     private final SchemaContext schemaContext;
69
70     /**
71      * The outter map maps module names to the map of the types for the module.
72      * The inner map maps the name of the concrete type to the JAVA
73      * <code>Type</code> (usually it is generated TO).
74      */
75     private Map<String, Map<String, Type>> genTypeDefsContextMap;
76
77     /**
78      * The map which maps schema paths to JAVA <code>Type</code>.
79      */
80     private final Map<SchemaPath, Type> referencedTypes;
81
82     /**
83      * Creates new instance of class <code>TypeProviderImpl</code>.
84      *
85      * @param schemaContext
86      *            contains the schema data red from YANG files
87      * @throws IllegalArgumentException
88      *             if <code>schemaContext</code> equal null.
89      */
90     public TypeProviderImpl(final SchemaContext schemaContext) {
91         Preconditions.checkArgument(schemaContext != null, "Schema Context cannot be null!");
92
93         this.schemaContext = schemaContext;
94         this.genTypeDefsContextMap = new HashMap<>();
95         this.referencedTypes = new HashMap<>();
96         resolveTypeDefsFromContext();
97     }
98
99     /**
100      * Puts <code>refType</code> to map with key <code>refTypePath</code>
101      *
102      * @param refTypePath
103      *            schema path used as the map key
104      * @param refType
105      *            type which represents the map value
106      * @throws IllegalArgumentException
107      *             <ul>
108      *             <li>if <code>refTypePath</code> equal null</li>
109      *             <li>if <code>refType</code> equal null</li>
110      *             </ul>
111      *
112      */
113     public void putReferencedType(final SchemaPath refTypePath, final Type refType) {
114         Preconditions.checkArgument(refTypePath != null,
115                 "Path reference of Enumeration Type Definition cannot be NULL!");
116         Preconditions.checkArgument(refType != null, "Reference to Enumeration Type cannot be NULL!");
117         referencedTypes.put(refTypePath, refType);
118     }
119
120     /**
121      *
122      * Converts basic YANG type <code>type</code> to JAVA <code>Type</code>.
123      *
124      * @param type
125      *            string with YANG name of type
126      * @return JAVA <code>Type</code> for YANG type <code>type</code>
127      * @see TypeProvider#javaTypeForYangType(String)
128      */
129     @Override
130     public Type javaTypeForYangType(String type) {
131         return BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForYangType(type);
132     }
133
134     @Override
135     public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode) {
136         return javaTypeForSchemaDefinitionType(typeDefinition, parentNode, null);
137     }
138
139     /**
140      * Converts schema definition type <code>typeDefinition</code> to JAVA
141      * <code>Type</code>
142      *
143      * @param typeDefinition
144      *            type definition which is converted to JAVA type
145      * @throws IllegalArgumentException
146      *             <ul>
147      *             <li>if <code>typeDefinition</code> equal null</li>
148      *             <li>if Q name of <code>typeDefinition</code> equal null</li>
149      *             <li>if name of <code>typeDefinition</code> equal null</li>
150      *             </ul>
151      */
152     @Override
153     public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode, Restrictions r) {
154         Type returnType = null;
155         Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
156         if (typeDefinition.getQName() == null) {
157             throw new IllegalArgumentException(
158                     "Type Definition cannot have non specified QName (QName cannot be NULL!)");
159         }
160         Preconditions.checkArgument(typeDefinition.getQName().getLocalName() != null,
161                 "Type Definitions Local Name cannot be NULL!");
162
163         if (typeDefinition instanceof ExtendedType) {
164             returnType = javaTypeForExtendedType(typeDefinition);
165         } else {
166             returnType = javaTypeForLeafrefOrIdentityRef(typeDefinition, parentNode);
167             if (returnType == null) {
168                 returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(typeDefinition,
169                         parentNode, r);
170             }
171         }
172         // TODO: add throw exception when we will be able to resolve ALL yang
173         // types!
174         // if (returnType == null) {
175         // throw new IllegalArgumentException("Type Provider can't resolve " +
176         // "type for specified Type Definition " + typedefName);
177         // }
178         return returnType;
179     }
180
181     /**
182      * Returns JAVA <code>Type</code> for instances of the type
183      * <code>LeafrefTypeDefinition</code> or
184      * <code>IdentityrefTypeDefinition</code>.
185      *
186      * @param typeDefinition
187      *            type definition which is converted to JAVA <code>Type</code>
188      * @return JAVA <code>Type</code> instance for <code>typeDefinition</code>
189      */
190     private Type javaTypeForLeafrefOrIdentityRef(TypeDefinition<?> typeDefinition, SchemaNode parentNode) {
191         if (typeDefinition instanceof LeafrefTypeDefinition) {
192             final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) typeDefinition;
193             return provideTypeForLeafref(leafref, parentNode);
194         } else if (typeDefinition instanceof IdentityrefTypeDefinition) {
195             final IdentityrefTypeDefinition idref = (IdentityrefTypeDefinition) typeDefinition;
196             return provideTypeForIdentityref(idref);
197         } else {
198             return null;
199         }
200     }
201
202     /**
203      * Returns JAVA <code>Type</code> for instances of the type
204      * <code>ExtendedType</code>.
205      *
206      * @param typeDefinition
207      *            type definition which is converted to JAVA <code>Type</code>
208      * @return JAVA <code>Type</code> instance for <code>typeDefinition</code>
209      */
210     private Type javaTypeForExtendedType(TypeDefinition<?> typeDefinition) {
211         final String typedefName = typeDefinition.getQName().getLocalName();
212         final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
213         Type returnType = null;
214         returnType = javaTypeForLeafrefOrIdentityRef(baseTypeDef, typeDefinition);
215         if (returnType == null) {
216             if (baseTypeDef instanceof EnumTypeDefinition) {
217                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypeDef;
218                 returnType = provideTypeForEnum(enumTypeDef, typedefName, typeDefinition);
219             } else {
220                 final Module module = findParentModule(schemaContext, typeDefinition);
221                 Restrictions r = BindingGeneratorUtil.getRestrictions(typeDefinition);
222                 if (module != null) {
223                     final Map<String, Type> genTOs = genTypeDefsContextMap.get(module.getName());
224                     if (genTOs != null) {
225                         returnType = genTOs.get(typedefName);
226                     }
227                     if (returnType == null) {
228                         returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
229                                 baseTypeDef, typeDefinition, r);
230                     }
231                 }
232             }
233         }
234         return returnType;
235         // TODO: add throw exception when we will be able to resolve ALL yang
236         // types!
237         // if (returnType == null) {
238         // throw new IllegalArgumentException("Type Provider can't resolve " +
239         // "type for specified Type Definition " + typedefName);
240         // }
241     }
242
243     /**
244      * Seeks for identity reference <code>idref</code> the JAVA
245      * <code>type</code>.<br />
246      * <br />
247      *
248      * <i>Example:<br />
249      * If identy which is referenced via <code>idref</code> has name <b>Idn</b>
250      * then returning type is <b>{@code Class<? extends Idn>}</b></i>
251      *
252      * @param idref
253      *            identityref type definition for which JAVA <code>Type</code>
254      *            is sought
255      * @return JAVA <code>Type</code> of the identity which is refrenced through
256      *         <code>idref</code>
257      */
258     private Type provideTypeForIdentityref(IdentityrefTypeDefinition idref) {
259         QName baseIdQName = idref.getIdentity().getQName();
260         Module module = schemaContext.findModuleByNamespaceAndRevision(baseIdQName.getNamespace(),
261                 baseIdQName.getRevision());
262         IdentitySchemaNode identity = null;
263         for (IdentitySchemaNode id : module.getIdentities()) {
264             if (id.getQName().equals(baseIdQName)) {
265                 identity = id;
266             }
267         }
268         Preconditions.checkArgument(identity != null, "Target identity '" + baseIdQName + "' do not exists");
269
270         final String basePackageName = moduleNamespaceToPackageName(module);
271         final String packageName = packageNameForGeneratedType(basePackageName, identity.getPath());
272         final String genTypeName = parseToClassName(identity.getQName().getLocalName());
273
274         Type baseType = Types.typeForClass(Class.class);
275         Type paramType = Types.wildcardTypeFor(packageName, genTypeName);
276         return Types.parameterizedTypeFor(baseType, paramType);
277     }
278
279     /**
280      * Converts <code>typeDefinition</code> to concrete JAVA <code>Type</code>.
281      *
282      * @param typeDefinition
283      *            type definition which should be converted to JAVA
284      *            <code>Type</code>
285      * @return JAVA <code>Type</code> which represents
286      *         <code>typeDefinition</code>
287      * @throws IllegalArgumentException
288      *             <ul>
289      *             <li>if <code>typeDefinition</code> equal null</li>
290      *             <li>if Q name of <code>typeDefinition</code></li>
291      *             <li>if name of <code>typeDefinition</code></li>
292      *             </ul>
293      */
294     public Type generatedTypeForExtendedDefinitionType(final TypeDefinition<?> typeDefinition,
295             final SchemaNode parentNode) {
296         Type returnType = null;
297         Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
298         if (typeDefinition.getQName() == null) {
299             throw new IllegalArgumentException(
300                     "Type Definition cannot have non specified QName (QName cannot be NULL!)");
301         }
302         Preconditions.checkArgument(typeDefinition.getQName().getLocalName() != null,
303                 "Type Definitions Local Name cannot be NULL!");
304
305         final String typedefName = typeDefinition.getQName().getLocalName();
306         if (typeDefinition instanceof ExtendedType) {
307             final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
308
309             if (!(baseTypeDef instanceof LeafrefTypeDefinition) && !(baseTypeDef instanceof IdentityrefTypeDefinition)) {
310                 final Module module = findParentModule(schemaContext, parentNode);
311
312                 if (module != null) {
313                     final Map<String, Type> genTOs = genTypeDefsContextMap.get(module.getName());
314                     if (genTOs != null) {
315                         returnType = genTOs.get(typedefName);
316                     }
317                 }
318             }
319         }
320         return returnType;
321     }
322
323     /**
324      * Gets base type definition for <code>extendTypeDef</code>. The method is
325      * recursivelly called until non <code>ExtendedType</code> type is found.
326      *
327      * @param extendTypeDef
328      *            type definition for which is the base type definition sought
329      * @return type definition which is base type for <code>extendTypeDef</code>
330      * @throws IllegalArgumentException
331      *             if <code>extendTypeDef</code> equal null
332      */
333     private TypeDefinition<?> baseTypeDefForExtendedType(final TypeDefinition<?> extendTypeDef) {
334         Preconditions.checkArgument(extendTypeDef != null, "Type Definiition reference cannot be NULL!");
335         final TypeDefinition<?> baseTypeDef = extendTypeDef.getBaseType();
336         if (baseTypeDef instanceof ExtendedType) {
337             return baseTypeDefForExtendedType(baseTypeDef);
338         } else {
339             return baseTypeDef;
340         }
341
342     }
343
344     /**
345      * Converts <code>leafrefType</code> to JAVA <code>Type</code>.
346      *
347      * The path of <code>leafrefType</code> is followed to find referenced node
348      * and its <code>Type</code> is returned.
349      *
350      * @param leafrefType
351      *            leafref type definition for which is the type sought
352      * @return JAVA <code>Type</code> of data schema node which is referenced in
353      *         <code>leafrefType</code>
354      * @throws IllegalArgumentException
355      *             <ul>
356      *             <li>if <code>leafrefType</code> equal null</li>
357      *             <li>if path statement of <code>leafrefType</code> equal null</li>
358      *             </ul>
359      *
360      */
361     public Type provideTypeForLeafref(final LeafrefTypeDefinition leafrefType, final SchemaNode parentNode) {
362         Type returnType = null;
363         Preconditions.checkArgument(leafrefType != null, "Leafref Type Definition reference cannot be NULL!");
364
365         Preconditions.checkArgument(leafrefType.getPathStatement() != null,
366                 "The Path Statement for Leafref Type Definition cannot be NULL!");
367
368         final RevisionAwareXPath xpath = leafrefType.getPathStatement();
369         final String strXPath = xpath.toString();
370
371         if (strXPath != null) {
372             if (strXPath.contains("[")) {
373                 returnType = Types.typeForClass(Object.class);
374             } else {
375                 final Module module = findParentModule(schemaContext, parentNode);
376                 if (module != null) {
377                     final SchemaNode dataNode;
378                     if (xpath.isAbsolute()) {
379                         dataNode = findDataSchemaNode(schemaContext, module, xpath);
380                     } else {
381                         dataNode = findDataSchemaNodeForRelativeXPath(schemaContext, module, parentNode, xpath);
382                     }
383
384                     if (leafContainsEnumDefinition(dataNode)) {
385                         returnType = referencedTypes.get(dataNode.getPath());
386                     } else if (leafListContainsEnumDefinition(dataNode)) {
387                         returnType = Types.listTypeFor(referencedTypes.get(dataNode.getPath()));
388                     } else {
389                         returnType = resolveTypeFromDataSchemaNode(dataNode);
390                     }
391                 }
392             }
393         }
394         return returnType;
395     }
396
397     /**
398      * Checks if <code>dataNode</code> is <code>LeafSchemaNode</code> and if it
399      * so then checks if it is of type <code>EnumTypeDefinition</code>.
400      *
401      * @param dataNode
402      *            data schema node for which is checked if it is leaf and if it
403      *            is of enum type
404      * @return boolean value
405      *         <ul>
406      *         <li>true - if <code>dataNode</code> is leaf of type enumeration</li>
407      *         <li>false - other cases</li>
408      *         </ul>
409      */
410     private boolean leafContainsEnumDefinition(final SchemaNode dataNode) {
411         if (dataNode instanceof LeafSchemaNode) {
412             final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
413             if (leaf.getType() instanceof EnumTypeDefinition) {
414                 return true;
415             }
416         }
417         return false;
418     }
419
420     /**
421      * Checks if <code>dataNode</code> is <code>LeafListSchemaNode</code> and if
422      * it so then checks if it is of type <code>EnumTypeDefinition</code>.
423      *
424      * @param dataNode
425      *            data schema node for which is checked if it is leaflist and if
426      *            it is of enum type
427      * @return boolean value
428      *         <ul>
429      *         <li>true - if <code>dataNode</code> is leaflist of type
430      *         enumeration</li>
431      *         <li>false - other cases</li>
432      *         </ul>
433      */
434     private boolean leafListContainsEnumDefinition(final SchemaNode dataNode) {
435         if (dataNode instanceof LeafListSchemaNode) {
436             final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
437             if (leafList.getType() instanceof EnumTypeDefinition) {
438                 return true;
439             }
440         }
441         return false;
442     }
443
444     /**
445      * Converts <code>enumTypeDef</code> to
446      * {@link org.opendaylight.yangtools.sal.binding.model.api.Enumeration
447      * enumeration}.
448      *
449      * @param enumTypeDef
450      *            enumeration type definition which is converted to enumeration
451      * @param enumName
452      *            string with name which is used as the enumeration name
453      * @return enumeration type which is built with data (name, enum values)
454      *         from <code>enumTypeDef</code>
455      * @throws IllegalArgumentException
456      *             <ul>
457      *             <li>if <code>enumTypeDef</code> equals null</li>
458      *             <li>if enum values of <code>enumTypeDef</code> equal null</li>
459      *             <li>if Q name of <code>enumTypeDef</code> equal null</li>
460      *             <li>if name of <code>enumTypeDef</code> equal null</li>
461      *             </ul>
462      */
463     private Enumeration provideTypeForEnum(final EnumTypeDefinition enumTypeDef, final String enumName,
464             final SchemaNode parentNode) {
465         Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
466         Preconditions.checkArgument(enumTypeDef.getValues() != null,
467                 "EnumTypeDefinition MUST contain at least ONE value definition!");
468         Preconditions.checkArgument(enumTypeDef.getQName() != null, "EnumTypeDefinition MUST contain NON-NULL QName!");
469         Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
470                 "Local Name in EnumTypeDefinition QName cannot be NULL!");
471
472         final String enumerationName = parseToClassName(enumName);
473
474         Module module = findParentModule(schemaContext, parentNode);
475         final String basePackageName = moduleNamespaceToPackageName(module);
476
477         final EnumBuilder enumBuilder = new EnumerationBuilderImpl(basePackageName, enumerationName);
478         enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
479         return enumBuilder.toInstance(null);
480     }
481
482     /**
483      * Adds enumeration to <code>typeBuilder</code>. The enumeration data are
484      * taken from <code>enumTypeDef</code>.
485      *
486      * @param enumTypeDef
487      *            enumeration type definition is source of enumeration data for
488      *            <code>typeBuilder</code>
489      * @param enumName
490      *            string with the name of enumeration
491      * @param typeBuilder
492      *            generated type builder to which is enumeration added
493      * @return enumeration type which contains enumeration data form
494      *         <code>enumTypeDef</code>
495      * @throws IllegalArgumentException
496      *             <ul>
497      *             <li>if <code>enumTypeDef</code> equals null</li>
498      *             <li>if enum values of <code>enumTypeDef</code> equal null</li>
499      *             <li>if Q name of <code>enumTypeDef</code> equal null</li>
500      *             <li>if name of <code>enumTypeDef</code> equal null</li>
501      *             <li>if name of <code>typeBuilder</code> equal null</li>
502      *             </ul>
503      *
504      */
505     private Enumeration addInnerEnumerationToTypeBuilder(final EnumTypeDefinition enumTypeDef, final String enumName,
506             final GeneratedTypeBuilderBase<?> typeBuilder) {
507         Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
508         Preconditions.checkArgument(enumTypeDef.getValues() != null,
509                 "EnumTypeDefinition MUST contain at least ONE value definition!");
510         Preconditions.checkArgument(enumTypeDef.getQName() != null, "EnumTypeDefinition MUST contain NON-NULL QName!");
511         Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
512                 "Local Name in EnumTypeDefinition QName cannot be NULL!");
513         Preconditions.checkArgument(typeBuilder != null, "Generated Type Builder reference cannot be NULL!");
514
515         final String enumerationName = parseToClassName(enumName);
516
517         final EnumBuilder enumBuilder = typeBuilder.addEnumeration(enumerationName);
518         enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
519         return enumBuilder.toInstance(enumBuilder);
520     }
521
522     /**
523      * Converts <code>dataNode</code> to JAVA <code>Type</code>.
524      *
525      * @param dataNode
526      *            contains information about YANG type
527      * @return JAVA <code>Type</code> representation of <code>dataNode</code>
528      */
529     private Type resolveTypeFromDataSchemaNode(final SchemaNode dataNode) {
530         Type returnType = null;
531         if (dataNode != null) {
532             if (dataNode instanceof LeafSchemaNode) {
533                 final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
534                 returnType = javaTypeForSchemaDefinitionType(leaf.getType(), leaf);
535             } else if (dataNode instanceof LeafListSchemaNode) {
536                 final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
537                 returnType = javaTypeForSchemaDefinitionType(leafList.getType(), leafList);
538             }
539         }
540         return returnType;
541     }
542
543     /**
544      * Passes through all modules and through all its type definitions and
545      * convert it to generated types.
546      *
547      * The modules are firstly sorted by mutual dependencies. The modules are
548      * sequentially passed. All type definitions of a module are at the
549      * beginning sorted so that type definition with less amount of references
550      * to other type definition are processed first.<br />
551      * For each module is created mapping record in the map
552      * {@link TypeProviderImpl#genTypeDefsContextMap genTypeDefsContextMap}
553      * which map current module name to the map which maps type names to
554      * returned types (generated types).
555      *
556      */
557     private void resolveTypeDefsFromContext() {
558         final Set<Module> modules = schemaContext.getModules();
559         Preconditions.checkArgument(modules != null, "Sef of Modules cannot be NULL!");
560         final Module[] modulesArray = new Module[modules.size()];
561         int i = 0;
562         for (Module modul : modules) {
563             modulesArray[i++] = modul;
564         }
565         final List<Module> modulesSortedByDependency = ModuleDependencySort.sort(modulesArray);
566
567         for (final Module module : modulesSortedByDependency) {
568             if (module == null) {
569                 continue;
570             }
571             final String moduleName = module.getName();
572             final String basePackageName = moduleNamespaceToPackageName(module);
573
574             final DataNodeIterator it = new DataNodeIterator(module);
575             final List<TypeDefinition<?>> typeDefinitions = it.allTypedefs();
576             final List<TypeDefinition<?>> listTypeDefinitions = sortTypeDefinitionAccordingDepth(typeDefinitions);
577
578             final Map<String, Type> typeMap = new HashMap<>();
579             genTypeDefsContextMap.put(moduleName, typeMap);
580
581             if ((listTypeDefinitions != null) && (basePackageName != null)) {
582                 for (final TypeDefinition<?> typedef : listTypeDefinitions) {
583                     typedefToGeneratedType(basePackageName, moduleName, typedef);
584                 }
585             }
586         }
587     }
588
589     /**
590      *
591      * @param basePackageName
592      *            string with name of package to which the module belongs
593      * @param moduleName
594      *            string with the name of the module for to which the
595      *            <code>typedef</code> belongs
596      * @param typedef
597      *            type definition of the node for which should be creted JAVA
598      *            <code>Type</code> (usually generated TO)
599      * @return JAVA <code>Type</code> representation of <code>typedef</code> or
600      *         <code>null</code> value if <code>basePackageName</code> or
601      *         <code>modulName</code> or <code>typedef</code> or Q name of
602      *         <code>typedef</code> equals <code>null</code>
603      */
604     private Type typedefToGeneratedType(final String basePackageName, final String moduleName,
605             final TypeDefinition<?> typedef) {
606         if ((basePackageName != null) && (moduleName != null) && (typedef != null) && (typedef.getQName() != null)) {
607
608             final String typedefName = typedef.getQName().getLocalName();
609             final TypeDefinition<?> innerTypeDefinition = typedef.getBaseType();
610             if (!(innerTypeDefinition instanceof LeafrefTypeDefinition)
611                     && !(innerTypeDefinition instanceof IdentityrefTypeDefinition)) {
612                 Type returnType = null;
613                 if (innerTypeDefinition instanceof ExtendedType) {
614                     ExtendedType innerExtendedType = (ExtendedType) innerTypeDefinition;
615                     returnType = provideGeneratedTOFromExtendedType(typedef, innerExtendedType, basePackageName);
616                 } else if (innerTypeDefinition instanceof UnionTypeDefinition) {
617                     final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForUnionTypeDef(basePackageName,
618                             (UnionTypeDefinition) innerTypeDefinition, typedefName, typedef);
619                     genTOBuilder.setIsUnion(true);
620                     returnType = genTOBuilder.toInstance();
621                 } else if (innerTypeDefinition instanceof EnumTypeDefinition) {
622                     final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) innerTypeDefinition;
623                     returnType = provideTypeForEnum(enumTypeDef, typedefName, typedef);
624
625                 } else if (innerTypeDefinition instanceof BitsTypeDefinition) {
626                     final BitsTypeDefinition bitsTypeDefinition = (BitsTypeDefinition) innerTypeDefinition;
627                     final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForBitsTypeDefinition(
628                             basePackageName, bitsTypeDefinition, typedefName);
629                     returnType = genTOBuilder.toInstance();
630                 } else {
631                     final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
632                             innerTypeDefinition, typedef);
633                     returnType = wrapJavaTypeIntoTO(basePackageName, typedef, javaType);
634                 }
635                 if (returnType != null) {
636                     final Map<String, Type> typeMap = genTypeDefsContextMap.get(moduleName);
637                     if (typeMap != null) {
638                         typeMap.put(typedefName, returnType);
639                     }
640                     return returnType;
641                 }
642             }
643         }
644         return null;
645     }
646
647     /**
648      * Wraps base YANG type to generated TO.
649      *
650      * @param basePackageName
651      *            string with name of package to which the module belongs
652      * @param typedef
653      *            type definition which is converted to the TO
654      * @param javaType
655      *            JAVA <code>Type</code> to which is <code>typedef</code> mapped
656      * @return generated transfer object which represent<code>javaType</code>
657      */
658     private GeneratedTransferObject wrapJavaTypeIntoTO(final String basePackageName, final TypeDefinition<?> typedef,
659             final Type javaType) {
660         if (javaType != null) {
661             final String propertyName = "value";
662
663             final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(basePackageName, typedef);
664             genTOBuilder.setRestrictions(BindingGeneratorUtil.getRestrictions(typedef));
665             final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty(propertyName);
666             genPropBuilder.setReturnType(javaType);
667             genTOBuilder.addEqualsIdentity(genPropBuilder);
668             genTOBuilder.addHashIdentity(genPropBuilder);
669             genTOBuilder.addToStringProperty(genPropBuilder);
670             if (javaType instanceof ConcreteType && "String".equals(javaType.getName()) && typedef instanceof ExtendedType) {
671                 final List<String> regExps = resolveRegExpressionsFromTypedef((ExtendedType) typedef);
672                 addStringRegExAsConstant(genTOBuilder, regExps);
673             }
674             return genTOBuilder.toInstance();
675         }
676         return null;
677     }
678
679     /**
680      * Converts output list of generated TO builders to one TO builder (first
681      * from list) which contains the remaining builders as its enclosing TO.
682      *
683      * @param basePackageName
684      *            string with name of package to which the module belongs
685      * @param typedef
686      *            type definition which should be of type
687      *            <code>UnionTypeDefinition</code>
688      * @param typeDefName
689      *            string with name for generated TO
690      * @return generated TO builder with the list of enclosed generated TO
691      *         builders
692      */
693     public GeneratedTOBuilder provideGeneratedTOBuilderForUnionTypeDef(final String basePackageName,
694             final UnionTypeDefinition typedef, String typeDefName, SchemaNode parentNode) {
695         final List<GeneratedTOBuilder> genTOBuilders = provideGeneratedTOBuildersForUnionTypeDef(basePackageName,
696                 typedef, typeDefName, parentNode);
697         GeneratedTOBuilder resultTOBuilder = null;
698         if (!genTOBuilders.isEmpty()) {
699             resultTOBuilder = genTOBuilders.get(0);
700             genTOBuilders.remove(0);
701             for (GeneratedTOBuilder genTOBuilder : genTOBuilders) {
702                 resultTOBuilder.addEnclosingTransferObject(genTOBuilder);
703             }
704         }
705         return resultTOBuilder;
706     }
707
708     /**
709      * Converts <code>typedef</code> to generated TO with
710      * <code>typeDefName</code>. Every union type from <code>typedef</code> is
711      * added to generated TO builder as property.
712      *
713      * @param basePackageName
714      *            string with name of package to which the module belongs
715      * @param typedef
716      *            type definition which should be of type
717      *            <code>UnionTypeDefinition</code>
718      * @param typeDefName
719      *            string with name for generated TO
720      * @return generated TO builder which represents <code>typedef</code>
721      * @throws IllegalArgumentException
722      *             <ul>
723      *             <li>if <code>basePackageName</code> equals null</li>
724      *             <li>if <code>typedef</code> equals null</li>
725      *             <li>if Q name of <code>typedef</code> equals null</li>
726      *             </ul>
727      */
728     public List<GeneratedTOBuilder> provideGeneratedTOBuildersForUnionTypeDef(final String basePackageName,
729             final UnionTypeDefinition typedef, final String typeDefName, final SchemaNode parentNode) {
730         Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL!");
731         Preconditions.checkArgument(typedef != null, "Type Definition cannot be NULL!");
732         Preconditions.checkArgument(typedef.getQName() != null,
733                 "Type Definition cannot have non specified QName (QName cannot be NULL!)");
734
735         final List<GeneratedTOBuilder> generatedTOBuilders = new ArrayList<>();
736
737         if (typedef != null) {
738             final List<TypeDefinition<?>> unionTypes = typedef.getTypes();
739
740             final GeneratedTOBuilder unionGenTOBuilder;
741             if (typeDefName != null && !typeDefName.isEmpty()) {
742                 final String typeName = parseToClassName(typeDefName);
743                 unionGenTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typeName);
744             } else {
745                 unionGenTOBuilder = typedefToTransferObject(basePackageName, typedef);
746             }
747             generatedTOBuilders.add(unionGenTOBuilder);
748             unionGenTOBuilder.setIsUnion(true);
749             final List<String> regularExpressions = new ArrayList<String>();
750             for (final TypeDefinition<?> unionType : unionTypes) {
751                 final String unionTypeName = unionType.getQName().getLocalName();
752                 if (unionType instanceof UnionType) {
753                     generatedTOBuilders.addAll(resolveUnionSubtypeAsUnion(unionGenTOBuilder, (UnionType) unionType,
754                             basePackageName, parentNode));
755                 } else if (unionType instanceof ExtendedType) {
756                     resolveExtendedSubtypeAsUnion(unionGenTOBuilder, (ExtendedType) unionType, unionTypeName,
757                             regularExpressions, parentNode);
758                 } else if (unionType instanceof EnumTypeDefinition) {
759                     final Enumeration enumeration = addInnerEnumerationToTypeBuilder((EnumTypeDefinition) unionType,
760                             unionTypeName, unionGenTOBuilder);
761                     updateUnionTypeAsProperty(unionGenTOBuilder, enumeration, unionTypeName);
762                 } else {
763                     final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
764                             unionType, parentNode);
765                     updateUnionTypeAsProperty(unionGenTOBuilder, javaType, unionTypeName);
766                 }
767             }
768             if (!regularExpressions.isEmpty()) {
769                 addStringRegExAsConstant(unionGenTOBuilder, regularExpressions);
770             }
771
772             storeGenTO(typedef, unionGenTOBuilder, parentNode);
773         }
774         return generatedTOBuilders;
775     }
776
777     /**
778      * Wraps code which handle case when union subtype is also of the type
779      * <code>UnionType</code>.
780      *
781      * In this case the new generated TO is created for union subtype (recursive
782      * call of method
783      * {@link #provideGeneratedTOBuildersForUnionTypeDef(String, TypeDefinition, String)
784      * provideGeneratedTOBuilderForUnionTypeDef} and in parent TO builder
785      * <code>parentUnionGenTOBuilder</code> is created property which type is
786      * equal to new generated TO.
787      *
788      * @param parentUnionGenTOBuilder
789      *            generated TO builder to which is the property with the child
790      *            union subtype added
791      * @param basePackageName
792      *            string with the name of the module package
793      * @param unionSubtype
794      *            type definition which represents union subtype
795      * @return list of generated TO builders. The number of the builders can be
796      *         bigger one due to recursive call of
797      *         <code>provideGeneratedTOBuildersForUnionTypeDef</code> method.
798      */
799     private List<GeneratedTOBuilder> resolveUnionSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
800             final UnionTypeDefinition unionSubtype, final String basePackageName, final SchemaNode parentNode) {
801         final String newTOBuilderName = provideAvailableNameForGenTOBuilder(parentUnionGenTOBuilder.getName());
802         final List<GeneratedTOBuilder> subUnionGenTOBUilders = provideGeneratedTOBuildersForUnionTypeDef(
803                 basePackageName, unionSubtype, newTOBuilderName, parentNode);
804
805         final GeneratedPropertyBuilder propertyBuilder;
806         propertyBuilder = parentUnionGenTOBuilder.addProperty(BindingGeneratorUtil
807                 .parseToValidParamName(newTOBuilderName));
808         propertyBuilder.setReturnType(subUnionGenTOBUilders.get(0));
809         parentUnionGenTOBuilder.addEqualsIdentity(propertyBuilder);
810         parentUnionGenTOBuilder.addToStringProperty(propertyBuilder);
811
812         return subUnionGenTOBUilders;
813     }
814
815     /**
816      * Wraps code which handle case when union subtype is of the type
817      * <code>ExtendedType</code>.
818      *
819      * If TO for this type already exists it is used for the creation of the
820      * property in <code>parentUnionGenTOBuilder</code>. In other case the base
821      * type is used for the property creation.
822      *
823      * @param parentUnionGenTOBuilder
824      *            generated TO builder in which new property is created
825      * @param unionSubtype
826      *            type definition of the <code>ExtendedType</code> type which
827      *            represents union subtype
828      * @param unionTypeName
829      *            string with the name for <code>unionSubtype</code>
830      * @param regularExpressions
831      *            list of strings with the regular expressions
832      */
833     private void resolveExtendedSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
834             final ExtendedType unionSubtype, final String unionTypeName, final List<String> regularExpressions,
835             final SchemaNode parentNode) {
836         final Type genTO = findGenTO(unionTypeName, parentNode);
837         if (genTO != null) {
838             updateUnionTypeAsProperty(parentUnionGenTOBuilder, genTO, genTO.getName());
839         } else {
840             final TypeDefinition<?> baseType = baseTypeDefForExtendedType(unionSubtype);
841             if (unionTypeName.equals(baseType.getQName().getLocalName())) {
842                 final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(baseType,
843                         parentNode);
844                 if (javaType != null) {
845                     updateUnionTypeAsProperty(parentUnionGenTOBuilder, javaType, unionTypeName);
846                 }
847             }
848             if (baseType instanceof StringType) {
849                 regularExpressions.addAll(resolveRegExpressionsFromTypedef(unionSubtype));
850             }
851         }
852     }
853
854     /**
855      * Searches for generated TO for <code>searchedTypeDef</code> type
856      * definition in {@link #genTypeDefsContextMap genTypeDefsContextMap}
857      *
858      * @param searchedTypeName
859      *            string with name of <code>searchedTypeDef</code>
860      * @return generated TO for <code>searchedTypeDef</code> or
861      *         <code>null</code> it it doesn't exist
862      */
863     private Type findGenTO(final String searchedTypeName, final SchemaNode parentNode) {
864         final Module typeModule = findParentModule(schemaContext, parentNode);
865         if (typeModule != null && typeModule.getName() != null) {
866             final Map<String, Type> genTOs = genTypeDefsContextMap.get(typeModule.getName());
867             if (genTOs != null) {
868                 return genTOs.get(searchedTypeName);
869             }
870         }
871         return null;
872     }
873
874     /**
875      * Stores generated TO created from <code>genTOBuilder</code> for
876      * <code>newTypeDef</code> to {@link #genTypeDefsContextMap
877      * genTypeDefsContextMap} if the module for <code>newTypeDef</code> exists
878      *
879      * @param newTypeDef
880      *            type definition for which is <code>genTOBuilder</code> created
881      * @param genTOBuilder
882      *            generated TO builder which is converted to generated TO and
883      *            stored
884      */
885     private void storeGenTO(TypeDefinition<?> newTypeDef, GeneratedTOBuilder genTOBuilder, SchemaNode parentNode) {
886         if (!(newTypeDef instanceof UnionType)) {
887             Map<String, Type> genTOsMap = null;
888             final Module parentModule = findParentModule(schemaContext, parentNode);
889             if (parentModule != null && parentModule.getName() != null) {
890                 genTOsMap = genTypeDefsContextMap.get(parentModule.getName());
891                 genTOsMap.put(newTypeDef.getQName().getLocalName(), genTOBuilder.toInstance());
892             }
893         }
894     }
895
896     /**
897      * Adds a new property with the name <code>propertyName</code> and with type
898      * <code>type</code> to <code>unonGenTransObject</code>.
899      *
900      * @param unionGenTransObject
901      *            generated TO to which should be property added
902      * @param type
903      *            JAVA <code>type</code> of the property which should be added
904      *            to <code>unionGentransObject</code>
905      * @param propertyName
906      *            string with name of property which should be added to
907      *            <code>unionGentransObject</code>
908      */
909     private void updateUnionTypeAsProperty(final GeneratedTOBuilder unionGenTransObject, final Type type,
910             final String propertyName) {
911         if (unionGenTransObject != null && type != null && !unionGenTransObject.containsProperty(propertyName)) {
912             final GeneratedPropertyBuilder propBuilder = unionGenTransObject
913                     .addProperty(parseToValidParamName(propertyName));
914             propBuilder.setReturnType(type);
915
916             unionGenTransObject.addEqualsIdentity(propBuilder);
917             unionGenTransObject.addHashIdentity(propBuilder);
918             unionGenTransObject.addToStringProperty(propBuilder);
919         }
920     }
921
922     /**
923      * Converts <code>typedef</code> to the generated TO builder.
924      *
925      * @param basePackageName
926      *            string with name of package to which the module belongs
927      * @param typedef
928      *            type definition from which is the generated TO builder created
929      * @return generated TO builder which contains data from
930      *         <code>typedef</code> and <code>basePackageName</code>
931      */
932     private GeneratedTOBuilder typedefToTransferObject(final String basePackageName, final TypeDefinition<?> typedef) {
933
934         final String packageName = packageNameForGeneratedType(basePackageName, typedef.getPath());
935         final String typeDefTOName = typedef.getQName().getLocalName();
936
937         if ((packageName != null) && (typedef != null) && (typeDefTOName != null)) {
938             final String genTOName = parseToClassName(typeDefTOName);
939             final GeneratedTOBuilder newType = new GeneratedTOBuilderImpl(packageName, genTOName);
940             newType.addComment(typedef.getDescription());
941             return newType;
942         }
943         return null;
944     }
945
946     /**
947      * Converts <code>typeDef</code> which should be of the type
948      * <code>BitsTypeDefinition</code> to <code>GeneratedTOBuilder</code>.
949      *
950      * All the bits of the typeDef are added to returning generated TO as
951      * properties.
952      *
953      * @param basePackageName
954      *            string with name of package to which the module belongs
955      * @param typeDef
956      *            type definition from which is the generated TO builder created
957      * @param typeDefName
958      *            string with the name for generated TO builder
959      * @return generated TO builder which represents <code>typeDef</code>
960      * @throws IllegalArgumentException
961      *             <ul>
962      *             <li>if <code>typeDef</code> equals null</li>
963      *             <li>if <code>basePackageName</code> equals null</li>
964      *             </ul>
965      */
966     public GeneratedTOBuilder provideGeneratedTOBuilderForBitsTypeDefinition(final String basePackageName,
967             final TypeDefinition<?> typeDef, String typeDefName) {
968
969         Preconditions.checkArgument(typeDef != null, "typeDef cannot be NULL!");
970         Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL!");
971
972         if (typeDef instanceof BitsTypeDefinition) {
973             BitsTypeDefinition bitsTypeDefinition = (BitsTypeDefinition) typeDef;
974
975             final String typeName = parseToClassName(typeDefName);
976             final GeneratedTOBuilder genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typeName);
977
978             final List<Bit> bitList = bitsTypeDefinition.getBits();
979             GeneratedPropertyBuilder genPropertyBuilder;
980             for (final Bit bit : bitList) {
981                 String name = bit.getName();
982                 genPropertyBuilder = genTOBuilder.addProperty(parseToValidParamName(name));
983                 genPropertyBuilder.setReadOnly(true);
984                 genPropertyBuilder.setReturnType(BaseYangTypes.BOOLEAN_TYPE);
985
986                 genTOBuilder.addEqualsIdentity(genPropertyBuilder);
987                 genTOBuilder.addHashIdentity(genPropertyBuilder);
988                 genTOBuilder.addToStringProperty(genPropertyBuilder);
989             }
990
991             return genTOBuilder;
992         }
993         return null;
994     }
995
996     /**
997      * Converts the pattern constraints from <code>typedef</code> to the list of
998      * the strings which represents these constraints.
999      *
1000      * @param typedef
1001      *            extended type in which are the pattern constraints sought
1002      * @return list of strings which represents the constraint patterns
1003      * @throws IllegalArgumentException
1004      *             if <code>typedef</code> equals null
1005      *
1006      */
1007     private List<String> resolveRegExpressionsFromTypedef(ExtendedType typedef) {
1008         final List<String> regExps = new ArrayList<String>();
1009         Preconditions.checkArgument(typedef != null, "typedef can't be null");
1010         final TypeDefinition<?> strTypeDef = baseTypeDefForExtendedType(typedef);
1011         if (strTypeDef instanceof StringType) {
1012             final List<PatternConstraint> patternConstraints = typedef.getPatternConstraints();
1013             if (!patternConstraints.isEmpty()) {
1014                 String regEx;
1015                 String modifiedRegEx;
1016                 for (PatternConstraint patternConstraint : patternConstraints) {
1017                     regEx = patternConstraint.getRegularExpression();
1018                     modifiedRegEx = StringEscapeUtils.escapeJava(regEx);
1019                     regExps.add(modifiedRegEx);
1020                 }
1021             }
1022         }
1023         return regExps;
1024     }
1025
1026     /**
1027      *
1028      * Adds to the <code>genTOBuilder</code> the constant which contains regular
1029      * expressions from the <code>regularExpressions</code>
1030      *
1031      * @param genTOBuilder
1032      *            generated TO builder to which are
1033      *            <code>regular expressions</code> added
1034      * @param regularExpressions
1035      *            list of string which represent regular expressions
1036      * @throws IllegalArgumentException
1037      *             <ul>
1038      *             <li>if <code>genTOBuilder</code> equals null</li>
1039      *             <li>if <code>regularExpressions</code> equals null</li>
1040      *             </ul>
1041      */
1042     private void addStringRegExAsConstant(GeneratedTOBuilder genTOBuilder, List<String> regularExpressions) {
1043         if (genTOBuilder == null) {
1044             throw new IllegalArgumentException("Generated transfer object builder can't be null");
1045         }
1046         if (regularExpressions == null) {
1047             throw new IllegalArgumentException("List of regular expressions can't be null");
1048         }
1049         if (!regularExpressions.isEmpty()) {
1050             genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), TypeConstants.PATTERN_CONSTANT_NAME,
1051                     regularExpressions);
1052         }
1053     }
1054
1055     /**
1056      * Creates generated TO with data about inner extended type
1057      * <code>innerExtendedType</code>, about the package name
1058      * <code>typedefName</code> and about the generated TO name
1059      * <code>typedefName</code>.
1060      *
1061      * It is supposed that <code>innerExtendedType</code> is already present in
1062      * {@link TypeProviderImpl#genTypeDefsContextMap genTypeDefsContextMap} to
1063      * be possible set it as extended type for the returning generated TO.
1064      *
1065      * @param innerExtendedType
1066      *            extended type which is part of some other extended type
1067      * @param basePackageName
1068      *            string with the package name of the module
1069      * @param typedefName
1070      *            string with the name for the generated TO
1071      * @return generated TO which extends generated TO for
1072      *         <code>innerExtendedType</code>
1073      * @throws IllegalArgumentException
1074      *             <ul>
1075      *             <li>if <code>extendedType</code> equals null</li>
1076      *             <li>if <code>basePackageName</code> equals null</li>
1077      *             <li>if <code>typedefName</code> equals null</li>
1078      *             </ul>
1079      */
1080     private GeneratedTransferObject provideGeneratedTOFromExtendedType(final TypeDefinition<?> typedef, final ExtendedType innerExtendedType,
1081             final String basePackageName) {
1082         Preconditions.checkArgument(innerExtendedType != null, "Extended type cannot be NULL!");
1083         Preconditions.checkArgument(basePackageName != null, "String with base package name cannot be NULL!");
1084
1085         final String typedefName = typedef.getQName().getLocalName();
1086         final String classTypedefName = parseToClassName(typedefName);
1087         final String innerTypeDef = innerExtendedType.getQName().getLocalName();
1088         final GeneratedTOBuilder genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, classTypedefName);
1089         Restrictions r = BindingGeneratorUtil.getRestrictions(typedef);
1090         genTOBuilder.setRestrictions(r);
1091
1092         if (baseTypeDefForExtendedType(innerExtendedType) instanceof UnionTypeDefinition) {
1093             genTOBuilder.setIsUnion(true);
1094         }
1095
1096         Map<String, Type> typeMap = null;
1097         final Module parentModule = findParentModule(schemaContext, innerExtendedType);
1098         if (parentModule != null) {
1099             typeMap = genTypeDefsContextMap.get(parentModule.getName());
1100         }
1101
1102         if (typeMap != null) {
1103             Type type = typeMap.get(innerTypeDef);
1104             if (type instanceof GeneratedTransferObject) {
1105                 genTOBuilder.setExtendsType((GeneratedTransferObject) type);
1106             }
1107         }
1108
1109         return genTOBuilder.toInstance();
1110     }
1111
1112     /**
1113      * Finds out for each type definition how many immersion (depth) is
1114      * necessary to get to the base type. Every type definition is inserted to
1115      * the map which key is depth and value is list of type definitions with
1116      * equal depth. In next step are lists from this map concatenated to one
1117      * list in ascending order according to their depth. All type definitions
1118      * are in the list behind all type definitions on which depends.
1119      *
1120      * @param unsortedTypeDefinitions
1121      *            list of type definitions which should be sorted by depth
1122      * @return list of type definitions sorted according their each other
1123      *         dependencies (type definitions which are depend on other type
1124      *         definitions are in list behind them).
1125      */
1126     private List<TypeDefinition<?>> sortTypeDefinitionAccordingDepth(
1127             final Collection<TypeDefinition<?>> unsortedTypeDefinitions) {
1128         List<TypeDefinition<?>> sortedTypeDefinition = new ArrayList<>();
1129
1130         Map<Integer, List<TypeDefinition<?>>> typeDefinitionsDepths = new TreeMap<>();
1131         for (TypeDefinition<?> unsortedTypeDefinition : unsortedTypeDefinitions) {
1132             final int depth = getTypeDefinitionDepth(unsortedTypeDefinition);
1133             List<TypeDefinition<?>> typeDefinitionsConcreteDepth = typeDefinitionsDepths.get(depth);
1134             if (typeDefinitionsConcreteDepth == null) {
1135                 typeDefinitionsConcreteDepth = new ArrayList<TypeDefinition<?>>();
1136                 typeDefinitionsDepths.put(depth, typeDefinitionsConcreteDepth);
1137             }
1138             typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
1139         }
1140         // keys are in ascending order
1141         Set<Integer> depths = typeDefinitionsDepths.keySet();
1142         for (Integer depth : depths) {
1143             sortedTypeDefinition.addAll(typeDefinitionsDepths.get(depth));
1144         }
1145
1146         return sortedTypeDefinition;
1147     }
1148
1149     /**
1150      * Returns how many immersion is necessary to get from the type definition
1151      * to the base type.
1152      *
1153      * @param typeDefinition
1154      *            type definition for which is depth sought.
1155      * @return number of immersions which are necessary to get from the type
1156      *         definition to the base type
1157      */
1158     private int getTypeDefinitionDepth(final TypeDefinition<?> typeDefinition) {
1159         if (typeDefinition == null) {
1160             return 1;
1161         }
1162         int depth = 1;
1163         TypeDefinition<?> baseType = typeDefinition.getBaseType();
1164
1165         if (baseType instanceof ExtendedType) {
1166             depth = depth + getTypeDefinitionDepth(typeDefinition.getBaseType());
1167         } else if (baseType instanceof UnionType) {
1168             List<TypeDefinition<?>> childTypeDefinitions = ((UnionType) baseType).getTypes();
1169             int maxChildDepth = 0;
1170             int childDepth = 1;
1171             for (TypeDefinition<?> childTypeDefinition : childTypeDefinitions) {
1172                 childDepth = childDepth + getTypeDefinitionDepth(childTypeDefinition);
1173                 if (childDepth > maxChildDepth) {
1174                     maxChildDepth = childDepth;
1175                 }
1176             }
1177             return maxChildDepth;
1178         }
1179         return depth;
1180     }
1181
1182     /**
1183      * Returns string which contains the same value as <code>name</code> but
1184      * integer suffix is incremented by one. If <code>name</code> contains no
1185      * number suffix then number 1 is added.
1186      *
1187      * @param name
1188      *            string with name of augmented node
1189      * @return string with the number suffix incremented by one (or 1 is added)
1190      */
1191     private String provideAvailableNameForGenTOBuilder(String name) {
1192         Pattern searchedPattern = Pattern.compile("[0-9]+\\z");
1193         Matcher mtch = searchedPattern.matcher(name);
1194         if (mtch.find()) {
1195             final int newSuffix = Integer.valueOf(name.substring(mtch.start())) + 1;
1196             return name.substring(0, mtch.start()) + newSuffix;
1197         } else {
1198             return name + 1;
1199         }
1200     }
1201
1202 }