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