5ee5279eed90aae2e9e11079ab03f7be13a1e231
[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.moduleNamespaceToPackageName;
11 import static org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil.packageNameForGeneratedType;
12 import static org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil.parseToValidParamName;
13 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
14 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNodeForRelativeXPath;
15 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
16
17 import com.google.common.base.Preconditions;
18 import com.google.common.collect.Sets;
19 import com.google.common.io.BaseEncoding;
20 import java.math.BigDecimal;
21 import java.math.BigInteger;
22 import java.net.URI;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.Date;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.TreeMap;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36 import org.apache.commons.lang3.StringEscapeUtils;
37 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
38 import org.opendaylight.yangtools.binding.generator.util.TypeConstants;
39 import org.opendaylight.yangtools.binding.generator.util.Types;
40 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.EnumerationBuilderImpl;
41 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedPropertyBuilderImpl;
42 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
43 import org.opendaylight.yangtools.sal.binding.generator.spi.TypeProvider;
44 import org.opendaylight.yangtools.sal.binding.model.api.AccessModifier;
45 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
46 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration;
47 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject;
48 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions;
49 import org.opendaylight.yangtools.sal.binding.model.api.Type;
50 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.EnumBuilder;
51 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedPropertyBuilder;
52 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
53 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
54 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.MethodSignatureBuilder;
55 import org.opendaylight.yangtools.yang.binding.BindingMapping;
56 import org.opendaylight.yangtools.yang.common.QName;
57 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
58 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
59 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
60 import org.opendaylight.yangtools.yang.model.api.Module;
61 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
62 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
63 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
64 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
65 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
66 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
67 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
68 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
69 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
70 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
71 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
72 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
73 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
74 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
75 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
76 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
77 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
78 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
79 import org.opendaylight.yangtools.yang.model.util.DataNodeIterator;
80 import org.opendaylight.yangtools.yang.model.util.EnumerationType;
81 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
82 import org.opendaylight.yangtools.yang.model.util.Int16;
83 import org.opendaylight.yangtools.yang.model.util.Int32;
84 import org.opendaylight.yangtools.yang.model.util.Int64;
85 import org.opendaylight.yangtools.yang.model.util.Int8;
86 import org.opendaylight.yangtools.yang.model.util.StringType;
87 import org.opendaylight.yangtools.yang.model.util.Uint16;
88 import org.opendaylight.yangtools.yang.model.util.Uint32;
89 import org.opendaylight.yangtools.yang.model.util.Uint64;
90 import org.opendaylight.yangtools.yang.model.util.Uint8;
91 import org.opendaylight.yangtools.yang.model.util.UnionType;
92 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
93
94 public final class TypeProviderImpl implements TypeProvider {
95     /**
96      * Contains the schema data red from YANG files.
97      */
98     private final SchemaContext schemaContext;
99
100     /**
101      * Map<moduleName, Map<moduleDate, Map<typeName, type>>>
102      */
103     private Map<String, Map<Date, Map<String, Type>>> genTypeDefsContextMap;
104
105     /**
106      * The map which maps schema paths to JAVA <code>Type</code>.
107      */
108     private final Map<SchemaPath, Type> referencedTypes;
109     private final Map<Module, Set<Type>> additionalTypes;
110
111     /**
112      * Creates new instance of class <code>TypeProviderImpl</code>.
113      *
114      * @param schemaContext
115      *            contains the schema data red from YANG files
116      * @throws IllegalArgumentException
117      *             if <code>schemaContext</code> equal null.
118      */
119     public TypeProviderImpl(final SchemaContext schemaContext) {
120         Preconditions.checkArgument(schemaContext != null, "Schema Context cannot be null!");
121
122         this.schemaContext = schemaContext;
123         this.genTypeDefsContextMap = new HashMap<>();
124         this.referencedTypes = new HashMap<>();
125         this.additionalTypes = new HashMap<>();
126         resolveTypeDefsFromContext();
127     }
128
129     /**
130      * Puts <code>refType</code> to map with key <code>refTypePath</code>
131      *
132      * @param refTypePath
133      *            schema path used as the map key
134      * @param refType
135      *            type which represents the map value
136      * @throws IllegalArgumentException
137      *             <ul>
138      *             <li>if <code>refTypePath</code> equal null</li>
139      *             <li>if <code>refType</code> equal null</li>
140      *             </ul>
141      *
142      */
143     public void putReferencedType(final SchemaPath refTypePath, final Type refType) {
144         Preconditions.checkArgument(refTypePath != null,
145                 "Path reference of Enumeration Type Definition cannot be NULL!");
146         Preconditions.checkArgument(refType != null, "Reference to Enumeration Type cannot be NULL!");
147         referencedTypes.put(refTypePath, refType);
148     }
149
150     public Map<Module, Set<Type>> getAdditionalTypes() {
151         return additionalTypes;
152     }
153
154     /**
155      *
156      * Converts basic YANG type <code>type</code> to JAVA <code>Type</code>.
157      *
158      * @param type
159      *            string with YANG name of type
160      * @return JAVA <code>Type</code> for YANG type <code>type</code>
161      * @see TypeProvider#javaTypeForYangType(String)
162      */
163     @Override
164     public Type javaTypeForYangType(String type) {
165         return BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForYangType(type);
166     }
167
168     @Override
169     public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode) {
170         return javaTypeForSchemaDefinitionType(typeDefinition, parentNode, null);
171     }
172
173     /**
174      * Converts schema definition type <code>typeDefinition</code> to JAVA
175      * <code>Type</code>
176      *
177      * @param typeDefinition
178      *            type definition which is converted to JAVA type
179      * @throws IllegalArgumentException
180      *             <ul>
181      *             <li>if <code>typeDefinition</code> equal null</li>
182      *             <li>if Qname of <code>typeDefinition</code> equal null</li>
183      *             <li>if name of <code>typeDefinition</code> equal null</li>
184      *             </ul>
185      */
186     @Override
187     public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
188             Restrictions r) {
189         Type returnType = null;
190         Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
191         if (typeDefinition.getQName() == null) {
192             throw new IllegalArgumentException(
193                     "Type Definition cannot have non specified QName (QName cannot be NULL!)");
194         }
195         Preconditions.checkArgument(typeDefinition.getQName().getLocalName() != null,
196                 "Type Definitions Local Name cannot be NULL!");
197
198         if (typeDefinition instanceof ExtendedType) {
199             returnType = javaTypeForExtendedType(typeDefinition);
200         } else {
201             returnType = javaTypeForLeafrefOrIdentityRef(typeDefinition, parentNode);
202             if (returnType == null) {
203                 returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForYangType(typeDefinition.getQName()
204                         .getLocalName());
205             }
206         }
207         // TODO: add throw exception when we will be able to resolve ALL yang
208         // types!
209         // if (returnType == null) {
210         // throw new IllegalArgumentException("Type Provider can't resolve " +
211         // "type for specified Type Definition " + typedefName);
212         // }
213         return returnType;
214     }
215
216     /**
217      * Returns JAVA <code>Type</code> for instances of the type
218      * <code>LeafrefTypeDefinition</code> or
219      * <code>IdentityrefTypeDefinition</code>.
220      *
221      * @param typeDefinition
222      *            type definition which is converted to JAVA <code>Type</code>
223      * @return JAVA <code>Type</code> instance for <code>typeDefinition</code>
224      */
225     private Type javaTypeForLeafrefOrIdentityRef(TypeDefinition<?> typeDefinition, SchemaNode parentNode) {
226         if (typeDefinition instanceof LeafrefTypeDefinition) {
227             final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) typeDefinition;
228             return provideTypeForLeafref(leafref, parentNode);
229         } else if (typeDefinition instanceof IdentityrefTypeDefinition) {
230             final IdentityrefTypeDefinition idref = (IdentityrefTypeDefinition) typeDefinition;
231             return provideTypeForIdentityref(idref);
232         } else {
233             return null;
234         }
235     }
236
237     /**
238      * Returns JAVA <code>Type</code> for instances of the type
239      * <code>ExtendedType</code>.
240      *
241      * @param typeDefinition
242      *            type definition which is converted to JAVA <code>Type</code>
243      * @return JAVA <code>Type</code> instance for <code>typeDefinition</code>
244      */
245     private Type javaTypeForExtendedType(TypeDefinition<?> typeDefinition) {
246         final String typedefName = typeDefinition.getQName().getLocalName();
247         final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
248         Type returnType = null;
249         returnType = javaTypeForLeafrefOrIdentityRef(baseTypeDef, typeDefinition);
250         if (returnType == null) {
251             if (baseTypeDef instanceof EnumTypeDefinition) {
252                 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypeDef;
253                 returnType = provideTypeForEnum(enumTypeDef, typedefName, typeDefinition);
254             } else {
255                 final Module module = findParentModule(schemaContext, typeDefinition);
256                 Restrictions r = BindingGeneratorUtil.getRestrictions(typeDefinition);
257                 if (module != null) {
258                     final Map<Date, Map<String, Type>> modulesByDate = genTypeDefsContextMap.get(module.getName());
259                     final Map<String, Type> genTOs = modulesByDate.get(module.getRevision());
260                     if (genTOs != null) {
261                         returnType = genTOs.get(typedefName);
262                     }
263                     if (returnType == null) {
264                         returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
265                                 baseTypeDef, typeDefinition, r);
266                     }
267                 }
268             }
269         }
270         return returnType;
271         // TODO: add throw exception when we will be able to resolve ALL yang
272         // types!
273         // if (returnType == null) {
274         // throw new IllegalArgumentException("Type Provider can't resolve " +
275         // "type for specified Type Definition " + typedefName);
276         // }
277     }
278
279     /**
280      * Seeks for identity reference <code>idref</code> the JAVA
281      * <code>type</code>.<br />
282      * <br />
283      *
284      * <i>Example:<br />
285      * If identy which is referenced via <code>idref</code> has name <b>Idn</b>
286      * then returning type is <b>{@code Class<? extends Idn>}</b></i>
287      *
288      * @param idref
289      *            identityref type definition for which JAVA <code>Type</code>
290      *            is sought
291      * @return JAVA <code>Type</code> of the identity which is refrenced through
292      *         <code>idref</code>
293      */
294     private Type provideTypeForIdentityref(IdentityrefTypeDefinition idref) {
295         QName baseIdQName = idref.getIdentity().getQName();
296         Module module = schemaContext.findModuleByNamespaceAndRevision(baseIdQName.getNamespace(),
297                 baseIdQName.getRevision());
298         IdentitySchemaNode identity = null;
299         for (IdentitySchemaNode id : module.getIdentities()) {
300             if (id.getQName().equals(baseIdQName)) {
301                 identity = id;
302             }
303         }
304         Preconditions.checkArgument(identity != null, "Target identity '" + baseIdQName + "' do not exists");
305
306         final String basePackageName = moduleNamespaceToPackageName(module);
307         final String packageName = packageNameForGeneratedType(basePackageName, identity.getPath());
308         final String genTypeName = BindingMapping.getClassName(identity.getQName());
309
310         Type baseType = Types.typeForClass(Class.class);
311         Type paramType = Types.wildcardTypeFor(packageName, genTypeName);
312         return Types.parameterizedTypeFor(baseType, paramType);
313     }
314
315     /**
316      * Converts <code>typeDefinition</code> to concrete JAVA <code>Type</code>.
317      *
318      * @param typeDefinition
319      *            type definition which should be converted to JAVA
320      *            <code>Type</code>
321      * @return JAVA <code>Type</code> which represents
322      *         <code>typeDefinition</code>
323      * @throws IllegalArgumentException
324      *             <ul>
325      *             <li>if <code>typeDefinition</code> equal null</li>
326      *             <li>if Q name of <code>typeDefinition</code></li>
327      *             <li>if name of <code>typeDefinition</code></li>
328      *             </ul>
329      */
330     public Type generatedTypeForExtendedDefinitionType(final TypeDefinition<?> typeDefinition,
331             final SchemaNode parentNode) {
332         Type returnType = null;
333         Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
334         if (typeDefinition.getQName() == null) {
335             throw new IllegalArgumentException(
336                     "Type Definition cannot have non specified QName (QName cannot be NULL!)");
337         }
338         Preconditions.checkArgument(typeDefinition.getQName().getLocalName() != null,
339                 "Type Definitions Local Name cannot be NULL!");
340
341         final String typedefName = typeDefinition.getQName().getLocalName();
342         if (typeDefinition instanceof ExtendedType) {
343             final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
344
345             if (!(baseTypeDef instanceof LeafrefTypeDefinition) && !(baseTypeDef instanceof IdentityrefTypeDefinition)) {
346                 final Module module = findParentModule(schemaContext, parentNode);
347
348                 if (module != null) {
349                     final Map<Date, Map<String, Type>> modulesByDate = genTypeDefsContextMap.get(module.getName());
350                     final Map<String, Type> genTOs = modulesByDate.get(module.getRevision());
351                     if (genTOs != null) {
352                         returnType = genTOs.get(typedefName);
353                     }
354                 }
355             }
356         }
357         return returnType;
358     }
359
360     /**
361      * Gets base type definition for <code>extendTypeDef</code>. The method is
362      * recursivelly called until non <code>ExtendedType</code> type is found.
363      *
364      * @param extendTypeDef
365      *            type definition for which is the base type definition sought
366      * @return type definition which is base type for <code>extendTypeDef</code>
367      * @throws IllegalArgumentException
368      *             if <code>extendTypeDef</code> equal null
369      */
370     private TypeDefinition<?> baseTypeDefForExtendedType(final TypeDefinition<?> extendTypeDef) {
371         Preconditions.checkArgument(extendTypeDef != null, "Type Definiition reference cannot be NULL!");
372         final TypeDefinition<?> baseTypeDef = extendTypeDef.getBaseType();
373         if (baseTypeDef == null) {
374             return extendTypeDef;
375         } else if (baseTypeDef instanceof ExtendedType) {
376             return baseTypeDefForExtendedType(baseTypeDef);
377         } else {
378             return baseTypeDef;
379         }
380
381     }
382
383     /**
384      * Converts <code>leafrefType</code> to JAVA <code>Type</code>.
385      *
386      * The path of <code>leafrefType</code> is followed to find referenced node
387      * and its <code>Type</code> is returned.
388      *
389      * @param leafrefType
390      *            leafref type definition for which is the type sought
391      * @return JAVA <code>Type</code> of data schema node which is referenced in
392      *         <code>leafrefType</code>
393      * @throws IllegalArgumentException
394      *             <ul>
395      *             <li>if <code>leafrefType</code> equal null</li>
396      *             <li>if path statement of <code>leafrefType</code> equal null</li>
397      *             </ul>
398      *
399      */
400     public Type provideTypeForLeafref(final LeafrefTypeDefinition leafrefType, final SchemaNode parentNode) {
401         Type returnType = null;
402         Preconditions.checkArgument(leafrefType != null, "Leafref Type Definition reference cannot be NULL!");
403
404         Preconditions.checkArgument(leafrefType.getPathStatement() != null,
405                 "The Path Statement for Leafref Type Definition cannot be NULL!");
406
407         final RevisionAwareXPath xpath = leafrefType.getPathStatement();
408         final String strXPath = xpath.toString();
409
410         if (strXPath != null) {
411             if (strXPath.contains("[")) {
412                 returnType = Types.typeForClass(Object.class);
413             } else {
414                 final Module module = findParentModule(schemaContext, parentNode);
415                 if (module != null) {
416                     final SchemaNode dataNode;
417                     if (xpath.isAbsolute()) {
418                         dataNode = findDataSchemaNode(schemaContext, module, xpath);
419                     } else {
420                         dataNode = findDataSchemaNodeForRelativeXPath(schemaContext, module, parentNode, xpath);
421                     }
422
423                     if (leafContainsEnumDefinition(dataNode)) {
424                         returnType = referencedTypes.get(dataNode.getPath());
425                     } else if (leafListContainsEnumDefinition(dataNode)) {
426                         returnType = Types.listTypeFor(referencedTypes.get(dataNode.getPath()));
427                     } else {
428                         returnType = resolveTypeFromDataSchemaNode(dataNode);
429                     }
430                 }
431             }
432         }
433         if (returnType == null) {
434             throw new IllegalArgumentException("Failed to find leafref target: " + strXPath);
435         }
436         return returnType;
437     }
438
439     /**
440      * Checks if <code>dataNode</code> is <code>LeafSchemaNode</code> and if it
441      * so then checks if it is of type <code>EnumTypeDefinition</code>.
442      *
443      * @param dataNode
444      *            data schema node for which is checked if it is leaf and if it
445      *            is of enum type
446      * @return boolean value
447      *         <ul>
448      *         <li>true - if <code>dataNode</code> is leaf of type enumeration</li>
449      *         <li>false - other cases</li>
450      *         </ul>
451      */
452     private boolean leafContainsEnumDefinition(final SchemaNode dataNode) {
453         if (dataNode instanceof LeafSchemaNode) {
454             final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
455             if (leaf.getType() instanceof EnumTypeDefinition) {
456                 return true;
457             }
458         }
459         return false;
460     }
461
462     /**
463      * Checks if <code>dataNode</code> is <code>LeafListSchemaNode</code> and if
464      * it so then checks if it is of type <code>EnumTypeDefinition</code>.
465      *
466      * @param dataNode
467      *            data schema node for which is checked if it is leaflist and if
468      *            it is of enum type
469      * @return boolean value
470      *         <ul>
471      *         <li>true - if <code>dataNode</code> is leaflist of type
472      *         enumeration</li>
473      *         <li>false - other cases</li>
474      *         </ul>
475      */
476     private boolean leafListContainsEnumDefinition(final SchemaNode dataNode) {
477         if (dataNode instanceof LeafListSchemaNode) {
478             final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
479             if (leafList.getType() instanceof EnumTypeDefinition) {
480                 return true;
481             }
482         }
483         return false;
484     }
485
486     /**
487      * Converts <code>enumTypeDef</code> to
488      * {@link org.opendaylight.yangtools.sal.binding.model.api.Enumeration
489      * enumeration}.
490      *
491      * @param enumTypeDef
492      *            enumeration type definition which is converted to enumeration
493      * @param enumName
494      *            string with name which is used as the enumeration name
495      * @return enumeration type which is built with data (name, enum values)
496      *         from <code>enumTypeDef</code>
497      * @throws IllegalArgumentException
498      *             <ul>
499      *             <li>if <code>enumTypeDef</code> equals null</li>
500      *             <li>if enum values of <code>enumTypeDef</code> equal null</li>
501      *             <li>if Q name of <code>enumTypeDef</code> equal null</li>
502      *             <li>if name of <code>enumTypeDef</code> equal null</li>
503      *             </ul>
504      */
505     private Enumeration provideTypeForEnum(final EnumTypeDefinition enumTypeDef, final String enumName,
506             final SchemaNode parentNode) {
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
514         final String enumerationName = BindingMapping.getClassName(enumName);
515
516         Module module = findParentModule(schemaContext, parentNode);
517         final String basePackageName = moduleNamespaceToPackageName(module);
518
519         final EnumBuilder enumBuilder = new EnumerationBuilderImpl(basePackageName, enumerationName);
520         enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
521         return enumBuilder.toInstance(null);
522     }
523
524     /**
525      * Adds enumeration to <code>typeBuilder</code>. The enumeration data are
526      * taken from <code>enumTypeDef</code>.
527      *
528      * @param enumTypeDef
529      *            enumeration type definition is source of enumeration data for
530      *            <code>typeBuilder</code>
531      * @param enumName
532      *            string with the name of enumeration
533      * @param typeBuilder
534      *            generated type builder to which is enumeration added
535      * @return enumeration type which contains enumeration data form
536      *         <code>enumTypeDef</code>
537      * @throws IllegalArgumentException
538      *             <ul>
539      *             <li>if <code>enumTypeDef</code> equals null</li>
540      *             <li>if enum values of <code>enumTypeDef</code> equal null</li>
541      *             <li>if Q name of <code>enumTypeDef</code> equal null</li>
542      *             <li>if name of <code>enumTypeDef</code> equal null</li>
543      *             <li>if name of <code>typeBuilder</code> equal null</li>
544      *             </ul>
545      *
546      */
547     private Enumeration addInnerEnumerationToTypeBuilder(final EnumTypeDefinition enumTypeDef, final String enumName,
548             final GeneratedTypeBuilderBase<?> typeBuilder) {
549         Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
550         Preconditions.checkArgument(enumTypeDef.getValues() != null,
551                 "EnumTypeDefinition MUST contain at least ONE value definition!");
552         Preconditions.checkArgument(enumTypeDef.getQName() != null, "EnumTypeDefinition MUST contain NON-NULL QName!");
553         Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
554                 "Local Name in EnumTypeDefinition QName cannot be NULL!");
555         Preconditions.checkArgument(typeBuilder != null, "Generated Type Builder reference cannot be NULL!");
556
557         final String enumerationName = BindingMapping.getClassName(enumName);
558
559         final EnumBuilder enumBuilder = typeBuilder.addEnumeration(enumerationName);
560         enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
561         return enumBuilder.toInstance(enumBuilder);
562     }
563
564     /**
565      * Converts <code>dataNode</code> to JAVA <code>Type</code>.
566      *
567      * @param dataNode
568      *            contains information about YANG type
569      * @return JAVA <code>Type</code> representation of <code>dataNode</code>
570      */
571     private Type resolveTypeFromDataSchemaNode(final SchemaNode dataNode) {
572         Type returnType = null;
573         if (dataNode != null) {
574             if (dataNode instanceof LeafSchemaNode) {
575                 final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
576                 returnType = javaTypeForSchemaDefinitionType(leaf.getType(), leaf);
577             } else if (dataNode instanceof LeafListSchemaNode) {
578                 final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
579                 returnType = javaTypeForSchemaDefinitionType(leafList.getType(), leafList);
580             }
581         }
582         return returnType;
583     }
584
585     /**
586      * Passes through all modules and through all its type definitions and
587      * convert it to generated types.
588      *
589      * The modules are firstly sorted by mutual dependencies. The modules are
590      * sequentially passed. All type definitions of a module are at the
591      * beginning sorted so that type definition with less amount of references
592      * to other type definition are processed first.<br />
593      * For each module is created mapping record in the map
594      * {@link TypeProviderImpl#genTypeDefsContextMap genTypeDefsContextMap}
595      * which map current module name to the map which maps type names to
596      * returned types (generated types).
597      *
598      */
599     private void resolveTypeDefsFromContext() {
600         final Set<Module> modules = schemaContext.getModules();
601         Preconditions.checkArgument(modules != null, "Sef of Modules cannot be NULL!");
602         final Module[] modulesArray = new Module[modules.size()];
603         int i = 0;
604         for (Module modul : modules) {
605             modulesArray[i++] = modul;
606         }
607         final List<Module> modulesSortedByDependency = ModuleDependencySort.sort(modulesArray);
608
609         for (final Module module : modulesSortedByDependency) {
610             Map<Date, Map<String, Type>> dateTypeMap = genTypeDefsContextMap.get(module.getName());
611             if (dateTypeMap == null) {
612                 dateTypeMap = new HashMap<>();
613             }
614             final Map<String, Type> typeMap = new HashMap<>();
615             dateTypeMap.put(module.getRevision(), typeMap);
616             genTypeDefsContextMap.put(module.getName(), dateTypeMap);
617         }
618
619         for (final Module module : modulesSortedByDependency) {
620             if (module == null) {
621                 continue;
622             }
623             final String basePackageName = moduleNamespaceToPackageName(module);
624
625             final DataNodeIterator it = new DataNodeIterator(module);
626             final List<TypeDefinition<?>> typeDefinitions = it.allTypedefs();
627             final List<TypeDefinition<?>> listTypeDefinitions = sortTypeDefinitionAccordingDepth(typeDefinitions);
628
629             if ((listTypeDefinitions != null) && (basePackageName != null)) {
630                 for (final TypeDefinition<?> typedef : listTypeDefinitions) {
631                     typedefToGeneratedType(basePackageName, module, typedef);
632                 }
633             }
634         }
635     }
636
637     /**
638      *
639      * @param basePackageName
640      *            string with name of package to which the module belongs
641      * @param moduleName
642      *            string with the name of the module for to which the
643      *            <code>typedef</code> belongs
644      * @param typedef
645      *            type definition of the node for which should be creted JAVA
646      *            <code>Type</code> (usually generated TO)
647      * @return JAVA <code>Type</code> representation of <code>typedef</code> or
648      *         <code>null</code> value if <code>basePackageName</code> or
649      *         <code>modulName</code> or <code>typedef</code> or Q name of
650      *         <code>typedef</code> equals <code>null</code>
651      */
652     private Type typedefToGeneratedType(final String basePackageName, final Module module,
653             final TypeDefinition<?> typedef) {
654         final String moduleName = module.getName();
655         final Date moduleRevision = module.getRevision();
656         if ((basePackageName != null) && (moduleName != null) && (typedef != null) && (typedef.getQName() != null)) {
657
658             final String typedefName = typedef.getQName().getLocalName();
659             final TypeDefinition<?> innerTypeDefinition = typedef.getBaseType();
660             if (!(innerTypeDefinition instanceof LeafrefTypeDefinition)
661                     && !(innerTypeDefinition instanceof IdentityrefTypeDefinition)) {
662                 Type returnType = null;
663                 if (innerTypeDefinition instanceof ExtendedType) {
664                     ExtendedType innerExtendedType = (ExtendedType) innerTypeDefinition;
665                     returnType = provideGeneratedTOFromExtendedType(typedef, innerExtendedType, basePackageName);
666                 } else if (innerTypeDefinition instanceof UnionTypeDefinition) {
667                     final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForUnionTypeDef(basePackageName,
668                             (UnionTypeDefinition) innerTypeDefinition, typedefName, typedef);
669                     genTOBuilder.setTypedef(true);
670                     genTOBuilder.setIsUnion(true);
671                     addUnitsToGenTO(genTOBuilder, typedef.getUnits());
672                     returnType = genTOBuilder.toInstance();
673                     // union builder
674                     GeneratedTOBuilder unionBuilder = new GeneratedTOBuilderImpl(genTOBuilder.getPackageName(),
675                             genTOBuilder.getName() + "Builder");
676                     unionBuilder.setIsUnionBuilder(true);
677                     MethodSignatureBuilder method = unionBuilder.addMethod("getDefaultInstance");
678                     method.setReturnType(returnType);
679                     method.addParameter(Types.STRING, "defaultValue");
680                     method.setAccessModifier(AccessModifier.PUBLIC);
681                     method.setStatic(true);
682                     Set<Type> types = additionalTypes.get(module);
683                     if (types == null) {
684                         types = Sets.<Type> newHashSet(unionBuilder.toInstance());
685                         additionalTypes.put(module, types);
686                     } else {
687                         types.add(unionBuilder.toInstance());
688                     }
689                 } else if (innerTypeDefinition instanceof EnumTypeDefinition) {
690                     final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) innerTypeDefinition;
691                     // TODO units for typedef enum
692                     returnType = provideTypeForEnum(enumTypeDef, typedefName, typedef);
693                 } else if (innerTypeDefinition instanceof BitsTypeDefinition) {
694                     final BitsTypeDefinition bitsTypeDefinition = (BitsTypeDefinition) innerTypeDefinition;
695                     final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForBitsTypeDefinition(
696                             basePackageName, bitsTypeDefinition, typedefName);
697                     genTOBuilder.setTypedef(true);
698                     addUnitsToGenTO(genTOBuilder, typedef.getUnits());
699                     returnType = genTOBuilder.toInstance();
700                 } else {
701                     final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
702                             innerTypeDefinition, typedef);
703                     returnType = wrapJavaTypeIntoTO(basePackageName, typedef, javaType);
704                 }
705                 if (returnType != null) {
706                     final Map<Date, Map<String, Type>> modulesByDate = genTypeDefsContextMap.get(moduleName);
707                     final Map<String, Type> typeMap = modulesByDate.get(moduleRevision);
708                     if (typeMap != null) {
709                         typeMap.put(typedefName, returnType);
710                     }
711                     return returnType;
712                 }
713             }
714         }
715         return null;
716     }
717
718     /**
719      * Wraps base YANG type to generated TO.
720      *
721      * @param basePackageName
722      *            string with name of package to which the module belongs
723      * @param typedef
724      *            type definition which is converted to the TO
725      * @param javaType
726      *            JAVA <code>Type</code> to which is <code>typedef</code> mapped
727      * @return generated transfer object which represent<code>javaType</code>
728      */
729     private GeneratedTransferObject wrapJavaTypeIntoTO(final String basePackageName, final TypeDefinition<?> typedef,
730             final Type javaType) {
731         Preconditions.checkNotNull(javaType, "javaType cannot be null");
732         final String propertyName = "value";
733
734         final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(basePackageName, typedef);
735         genTOBuilder.setRestrictions(BindingGeneratorUtil.getRestrictions(typedef));
736         final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty(propertyName);
737         genPropBuilder.setReturnType(javaType);
738         genTOBuilder.addEqualsIdentity(genPropBuilder);
739         genTOBuilder.addHashIdentity(genPropBuilder);
740         genTOBuilder.addToStringProperty(genPropBuilder);
741         if (javaType instanceof ConcreteType && "String".equals(javaType.getName()) && typedef instanceof ExtendedType) {
742             final List<String> regExps = resolveRegExpressionsFromTypedef((ExtendedType) typedef);
743             addStringRegExAsConstant(genTOBuilder, regExps);
744         }
745         addUnitsToGenTO(genTOBuilder, typedef.getUnits());
746         genTOBuilder.setTypedef(true);
747         return genTOBuilder.toInstance();
748     }
749
750     /**
751      * Converts output list of generated TO builders to one TO builder (first
752      * from list) which contains the remaining builders as its enclosing TO.
753      *
754      * @param basePackageName
755      *            string with name of package to which the module belongs
756      * @param typedef
757      *            type definition which should be of type
758      *            <code>UnionTypeDefinition</code>
759      * @param typeDefName
760      *            string with name for generated TO
761      * @return generated TO builder with the list of enclosed generated TO
762      *         builders
763      */
764     public GeneratedTOBuilder provideGeneratedTOBuilderForUnionTypeDef(final String basePackageName,
765             final UnionTypeDefinition typedef, String typeDefName, SchemaNode parentNode) {
766         final List<GeneratedTOBuilder> genTOBuilders = provideGeneratedTOBuildersForUnionTypeDef(basePackageName,
767                 typedef, typeDefName, parentNode);
768         GeneratedTOBuilder resultTOBuilder = null;
769         if (!genTOBuilders.isEmpty()) {
770             resultTOBuilder = genTOBuilders.remove(0);
771             for (GeneratedTOBuilder genTOBuilder : genTOBuilders) {
772                 resultTOBuilder.addEnclosingTransferObject(genTOBuilder);
773             }
774         }
775
776         final GeneratedPropertyBuilder genPropBuilder = resultTOBuilder.addProperty("value");
777         genPropBuilder.setReturnType(Types.primitiveType("char[]", null));
778         resultTOBuilder.addEqualsIdentity(genPropBuilder);
779         resultTOBuilder.addHashIdentity(genPropBuilder);
780         resultTOBuilder.addToStringProperty(genPropBuilder);
781
782         return resultTOBuilder;
783     }
784
785     /**
786      * Converts <code>typedef</code> to generated TO with
787      * <code>typeDefName</code>. Every union type from <code>typedef</code> is
788      * added to generated TO builder as property.
789      *
790      * @param basePackageName
791      *            string with name of package to which the module belongs
792      * @param typedef
793      *            type definition which should be of type
794      *            <code>UnionTypeDefinition</code>
795      * @param typeDefName
796      *            string with name for generated TO
797      * @return generated TO builder which represents <code>typedef</code>
798      * @throws NullPointerException
799      *             <ul>
800      *             <li>if <code>basePackageName</code> is null</li>
801      *             <li>if <code>typedef</code> is null</li>
802      *             <li>if Qname of <code>typedef</code> is null</li>
803      *             </ul>
804      */
805     public List<GeneratedTOBuilder> provideGeneratedTOBuildersForUnionTypeDef(final String basePackageName,
806             final UnionTypeDefinition typedef, final String typeDefName, final SchemaNode parentNode) {
807         Preconditions.checkNotNull(basePackageName, "Base Package Name cannot be NULL!");
808         Preconditions.checkNotNull(typedef, "Type Definition cannot be NULL!");
809         Preconditions.checkNotNull(typedef.getQName(), "Type definition QName cannot be NULL!");
810
811         final List<GeneratedTOBuilder> generatedTOBuilders = new ArrayList<>();
812         final List<TypeDefinition<?>> unionTypes = typedef.getTypes();
813
814         final GeneratedTOBuilder unionGenTOBuilder;
815         if (typeDefName != null && !typeDefName.isEmpty()) {
816             final String typeName = BindingMapping.getClassName(typeDefName);
817             unionGenTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typeName);
818         } else {
819             unionGenTOBuilder = typedefToTransferObject(basePackageName, typedef);
820         }
821
822         generatedTOBuilders.add(unionGenTOBuilder);
823         unionGenTOBuilder.setIsUnion(true);
824         final List<String> regularExpressions = new ArrayList<String>();
825         for (final TypeDefinition<?> unionType : unionTypes) {
826             final String unionTypeName = unionType.getQName().getLocalName();
827             if (unionType instanceof UnionType) {
828                 generatedTOBuilders.addAll(resolveUnionSubtypeAsUnion(unionGenTOBuilder, (UnionType) unionType,
829                         basePackageName, parentNode));
830             } else if (unionType instanceof ExtendedType) {
831                 resolveExtendedSubtypeAsUnion(unionGenTOBuilder, (ExtendedType) unionType, regularExpressions,
832                         parentNode);
833             } else if (unionType instanceof EnumTypeDefinition) {
834                 final Enumeration enumeration = addInnerEnumerationToTypeBuilder((EnumTypeDefinition) unionType,
835                         unionTypeName, unionGenTOBuilder);
836                 updateUnionTypeAsProperty(unionGenTOBuilder, enumeration, unionTypeName);
837             } else {
838                 final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(unionType,
839                         parentNode);
840                 updateUnionTypeAsProperty(unionGenTOBuilder, javaType, unionTypeName);
841             }
842         }
843         if (!regularExpressions.isEmpty()) {
844             addStringRegExAsConstant(unionGenTOBuilder, regularExpressions);
845         }
846
847         storeGenTO(typedef, unionGenTOBuilder, parentNode);
848
849         return generatedTOBuilders;
850     }
851
852     /**
853      * Wraps code which handle case when union subtype is also of the type
854      * <code>UnionType</code>.
855      *
856      * In this case the new generated TO is created for union subtype (recursive
857      * call of method
858      * {@link #provideGeneratedTOBuildersForUnionTypeDef(String, TypeDefinition, String)
859      * provideGeneratedTOBuilderForUnionTypeDef} and in parent TO builder
860      * <code>parentUnionGenTOBuilder</code> is created property which type is
861      * equal to new generated TO.
862      *
863      * @param parentUnionGenTOBuilder
864      *            generated TO builder to which is the property with the child
865      *            union subtype added
866      * @param basePackageName
867      *            string with the name of the module package
868      * @param unionSubtype
869      *            type definition which represents union subtype
870      * @return list of generated TO builders. The number of the builders can be
871      *         bigger one due to recursive call of
872      *         <code>provideGeneratedTOBuildersForUnionTypeDef</code> method.
873      */
874     private List<GeneratedTOBuilder> resolveUnionSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
875             final UnionTypeDefinition unionSubtype, final String basePackageName, final SchemaNode parentNode) {
876         final String newTOBuilderName = provideAvailableNameForGenTOBuilder(parentUnionGenTOBuilder.getName());
877         final List<GeneratedTOBuilder> subUnionGenTOBUilders = provideGeneratedTOBuildersForUnionTypeDef(
878                 basePackageName, unionSubtype, newTOBuilderName, parentNode);
879
880         final GeneratedPropertyBuilder propertyBuilder;
881         propertyBuilder = parentUnionGenTOBuilder.addProperty(BindingGeneratorUtil
882                 .parseToValidParamName(newTOBuilderName));
883         propertyBuilder.setReturnType(subUnionGenTOBUilders.get(0));
884         parentUnionGenTOBuilder.addEqualsIdentity(propertyBuilder);
885         parentUnionGenTOBuilder.addToStringProperty(propertyBuilder);
886
887         return subUnionGenTOBUilders;
888     }
889
890     /**
891      * Wraps code which handle case when union subtype is of the type
892      * <code>ExtendedType</code>.
893      *
894      * If TO for this type already exists it is used for the creation of the
895      * property in <code>parentUnionGenTOBuilder</code>. In other case the base
896      * type is used for the property creation.
897      *
898      * @param parentUnionGenTOBuilder
899      *            generated TO builder in which new property is created
900      * @param unionSubtype
901      *            type definition of the <code>ExtendedType</code> type which
902      *            represents union subtype
903      * @param unionTypeName
904      *            string with the name for <code>unionSubtype</code>
905      * @param regularExpressions
906      *            list of strings with the regular expressions
907      */
908     private void resolveExtendedSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
909             final ExtendedType unionSubtype, final List<String> regularExpressions,
910             final SchemaNode parentNode) {
911         final String unionTypeName = unionSubtype.getQName().getLocalName();
912         final Type genTO = findGenTO(unionTypeName, unionSubtype);
913         if (genTO != null) {
914             updateUnionTypeAsProperty(parentUnionGenTOBuilder, genTO, genTO.getName());
915         } else {
916             final TypeDefinition<?> baseType = baseTypeDefForExtendedType(unionSubtype);
917             if (unionTypeName.equals(baseType.getQName().getLocalName())) {
918                 final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(baseType,
919                         parentNode);
920                 if (javaType != null) {
921                     updateUnionTypeAsProperty(parentUnionGenTOBuilder, javaType, unionTypeName);
922                 }
923             }
924             if (baseType instanceof StringType) {
925                 regularExpressions.addAll(resolveRegExpressionsFromTypedef(unionSubtype));
926             }
927         }
928     }
929
930     /**
931      * Searches for generated TO for <code>searchedTypeDef</code> type
932      * definition in {@link #genTypeDefsContextMap genTypeDefsContextMap}
933      *
934      * @param searchedTypeName
935      *            string with name of <code>searchedTypeDef</code>
936      * @return generated TO for <code>searchedTypeDef</code> or
937      *         <code>null</code> it it doesn't exist
938      */
939     private Type findGenTO(final String searchedTypeName, final SchemaNode parentNode) {
940         final Module typeModule = findParentModule(schemaContext, parentNode);
941         if (typeModule != null && typeModule.getName() != null) {
942             final Map<Date, Map<String, Type>> modulesByDate = genTypeDefsContextMap.get(typeModule.getName());
943             final Map<String, Type> genTOs = modulesByDate.get(typeModule.getRevision());
944             if (genTOs != null) {
945                 return genTOs.get(searchedTypeName);
946             }
947         }
948         return null;
949     }
950
951     /**
952      * Stores generated TO created from <code>genTOBuilder</code> for
953      * <code>newTypeDef</code> to {@link #genTypeDefsContextMap
954      * genTypeDefsContextMap} if the module for <code>newTypeDef</code> exists
955      *
956      * @param newTypeDef
957      *            type definition for which is <code>genTOBuilder</code> created
958      * @param genTOBuilder
959      *            generated TO builder which is converted to generated TO and
960      *            stored
961      */
962     private void storeGenTO(TypeDefinition<?> newTypeDef, GeneratedTOBuilder genTOBuilder, SchemaNode parentNode) {
963         if (!(newTypeDef instanceof UnionType)) {
964
965             final Module parentModule = findParentModule(schemaContext, parentNode);
966             if (parentModule != null && parentModule.getName() != null) {
967                 Map<Date, Map<String, Type>> modulesByDate = genTypeDefsContextMap.get(parentModule.getName());
968                 Map<String, Type> genTOsMap = modulesByDate.get(parentModule.getRevision());
969                 genTOsMap.put(newTypeDef.getQName().getLocalName(), genTOBuilder.toInstance());
970             }
971         }
972     }
973
974     /**
975      * Adds a new property with the name <code>propertyName</code> and with type
976      * <code>type</code> to <code>unonGenTransObject</code>.
977      *
978      * @param unionGenTransObject
979      *            generated TO to which should be property added
980      * @param type
981      *            JAVA <code>type</code> of the property which should be added
982      *            to <code>unionGentransObject</code>
983      * @param propertyName
984      *            string with name of property which should be added to
985      *            <code>unionGentransObject</code>
986      */
987     private void updateUnionTypeAsProperty(final GeneratedTOBuilder unionGenTransObject, final Type type,
988             final String propertyName) {
989         if (unionGenTransObject != null && type != null && !unionGenTransObject.containsProperty(propertyName)) {
990             final GeneratedPropertyBuilder propBuilder = unionGenTransObject
991                     .addProperty(parseToValidParamName(propertyName));
992             propBuilder.setReturnType(type);
993
994             unionGenTransObject.addEqualsIdentity(propBuilder);
995             unionGenTransObject.addHashIdentity(propBuilder);
996             unionGenTransObject.addToStringProperty(propBuilder);
997         }
998     }
999
1000     /**
1001      * Converts <code>typedef</code> to the generated TO builder.
1002      *
1003      * @param basePackageName
1004      *            string with name of package to which the module belongs
1005      * @param typedef
1006      *            type definition from which is the generated TO builder created
1007      * @return generated TO builder which contains data from
1008      *         <code>typedef</code> and <code>basePackageName</code>
1009      */
1010     private GeneratedTOBuilder typedefToTransferObject(final String basePackageName, final TypeDefinition<?> typedef) {
1011
1012         final String packageName = packageNameForGeneratedType(basePackageName, typedef.getPath());
1013         final String typeDefTOName = typedef.getQName().getLocalName();
1014
1015         if ((packageName != null) && (typedef != null) && (typeDefTOName != null)) {
1016             final String genTOName = BindingMapping.getClassName(typeDefTOName);
1017             final GeneratedTOBuilder newType = new GeneratedTOBuilderImpl(packageName, genTOName);
1018             newType.addComment(typedef.getDescription());
1019             return newType;
1020         }
1021         return null;
1022     }
1023
1024     /**
1025      * Converts <code>typeDef</code> which should be of the type
1026      * <code>BitsTypeDefinition</code> to <code>GeneratedTOBuilder</code>.
1027      *
1028      * All the bits of the typeDef are added to returning generated TO as
1029      * properties.
1030      *
1031      * @param basePackageName
1032      *            string with name of package to which the module belongs
1033      * @param typeDef
1034      *            type definition from which is the generated TO builder created
1035      * @param typeDefName
1036      *            string with the name for generated TO builder
1037      * @return generated TO builder which represents <code>typeDef</code>
1038      * @throws IllegalArgumentException
1039      *             <ul>
1040      *             <li>if <code>typeDef</code> equals null</li>
1041      *             <li>if <code>basePackageName</code> equals null</li>
1042      *             </ul>
1043      */
1044     public GeneratedTOBuilder provideGeneratedTOBuilderForBitsTypeDefinition(final String basePackageName,
1045             final TypeDefinition<?> typeDef, String typeDefName) {
1046
1047         Preconditions.checkArgument(typeDef != null, "typeDef cannot be NULL!");
1048         Preconditions.checkArgument(basePackageName != null, "Base Package Name cannot be NULL!");
1049
1050         if (typeDef instanceof BitsTypeDefinition) {
1051             BitsTypeDefinition bitsTypeDefinition = (BitsTypeDefinition) typeDef;
1052
1053             final String typeName = BindingMapping.getClassName(typeDefName);
1054             final GeneratedTOBuilder genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, typeName);
1055
1056             final List<Bit> bitList = bitsTypeDefinition.getBits();
1057             GeneratedPropertyBuilder genPropertyBuilder;
1058             for (final Bit bit : bitList) {
1059                 String name = bit.getName();
1060                 genPropertyBuilder = genTOBuilder.addProperty(parseToValidParamName(name));
1061                 genPropertyBuilder.setReadOnly(true);
1062                 genPropertyBuilder.setReturnType(BaseYangTypes.BOOLEAN_TYPE);
1063
1064                 genTOBuilder.addEqualsIdentity(genPropertyBuilder);
1065                 genTOBuilder.addHashIdentity(genPropertyBuilder);
1066                 genTOBuilder.addToStringProperty(genPropertyBuilder);
1067             }
1068
1069             return genTOBuilder;
1070         }
1071         return null;
1072     }
1073
1074     /**
1075      * Converts the pattern constraints from <code>typedef</code> to the list of
1076      * the strings which represents these constraints.
1077      *
1078      * @param typedef
1079      *            extended type in which are the pattern constraints sought
1080      * @return list of strings which represents the constraint patterns
1081      * @throws IllegalArgumentException
1082      *             if <code>typedef</code> equals null
1083      *
1084      */
1085     private List<String> resolveRegExpressionsFromTypedef(ExtendedType typedef) {
1086         final List<String> regExps = new ArrayList<String>();
1087         Preconditions.checkArgument(typedef != null, "typedef can't be null");
1088         final TypeDefinition<?> strTypeDef = baseTypeDefForExtendedType(typedef);
1089         if (strTypeDef instanceof StringType) {
1090             final List<PatternConstraint> patternConstraints = typedef.getPatternConstraints();
1091             if (!patternConstraints.isEmpty()) {
1092                 String regEx;
1093                 String modifiedRegEx;
1094                 for (PatternConstraint patternConstraint : patternConstraints) {
1095                     regEx = patternConstraint.getRegularExpression();
1096                     modifiedRegEx = StringEscapeUtils.escapeJava(regEx);
1097                     regExps.add(modifiedRegEx);
1098                 }
1099             }
1100         }
1101         return regExps;
1102     }
1103
1104     /**
1105      *
1106      * Adds to the <code>genTOBuilder</code> the constant which contains regular
1107      * expressions from the <code>regularExpressions</code>
1108      *
1109      * @param genTOBuilder
1110      *            generated TO builder to which are
1111      *            <code>regular expressions</code> added
1112      * @param regularExpressions
1113      *            list of string which represent regular expressions
1114      * @throws IllegalArgumentException
1115      *             <ul>
1116      *             <li>if <code>genTOBuilder</code> equals null</li>
1117      *             <li>if <code>regularExpressions</code> equals null</li>
1118      *             </ul>
1119      */
1120     private void addStringRegExAsConstant(GeneratedTOBuilder genTOBuilder, List<String> regularExpressions) {
1121         if (genTOBuilder == null) {
1122             throw new IllegalArgumentException("Generated transfer object builder can't be null");
1123         }
1124         if (regularExpressions == null) {
1125             throw new IllegalArgumentException("List of regular expressions can't be null");
1126         }
1127         if (!regularExpressions.isEmpty()) {
1128             genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), TypeConstants.PATTERN_CONSTANT_NAME,
1129                     regularExpressions);
1130         }
1131     }
1132
1133     /**
1134      * Creates generated TO with data about inner extended type
1135      * <code>innerExtendedType</code>, about the package name
1136      * <code>typedefName</code> and about the generated TO name
1137      * <code>typedefName</code>.
1138      *
1139      * It is supposed that <code>innerExtendedType</code> is already present in
1140      * {@link TypeProviderImpl#genTypeDefsContextMap genTypeDefsContextMap} to
1141      * be possible set it as extended type for the returning generated TO.
1142      *
1143      * @param innerExtendedType
1144      *            extended type which is part of some other extended type
1145      * @param basePackageName
1146      *            string with the package name of the module
1147      * @param typedefName
1148      *            string with the name for the generated TO
1149      * @return generated TO which extends generated TO for
1150      *         <code>innerExtendedType</code>
1151      * @throws IllegalArgumentException
1152      *             <ul>
1153      *             <li>if <code>extendedType</code> equals null</li>
1154      *             <li>if <code>basePackageName</code> equals null</li>
1155      *             <li>if <code>typedefName</code> equals null</li>
1156      *             </ul>
1157      */
1158     private GeneratedTransferObject provideGeneratedTOFromExtendedType(final TypeDefinition<?> typedef,
1159             final ExtendedType innerExtendedType, final String basePackageName) {
1160         Preconditions.checkArgument(innerExtendedType != null, "Extended type cannot be NULL!");
1161         Preconditions.checkArgument(basePackageName != null, "String with base package name cannot be NULL!");
1162
1163         final String typedefName = typedef.getQName().getLocalName();
1164         final String classTypedefName = BindingMapping.getClassName(typedefName);
1165         final String innerTypeDef = innerExtendedType.getQName().getLocalName();
1166         final GeneratedTOBuilder genTOBuilder = new GeneratedTOBuilderImpl(basePackageName, classTypedefName);
1167         genTOBuilder.setTypedef(true);
1168         Restrictions r = BindingGeneratorUtil.getRestrictions(typedef);
1169         genTOBuilder.setRestrictions(r);
1170
1171         if (baseTypeDefForExtendedType(innerExtendedType) instanceof UnionTypeDefinition) {
1172             genTOBuilder.setIsUnion(true);
1173         }
1174
1175         Map<Date, Map<String, Type>> modulesByDate = null;
1176         Map<String, Type> typeMap = null;
1177         final Module parentModule = findParentModule(schemaContext, innerExtendedType);
1178         if (parentModule != null) {
1179             modulesByDate = genTypeDefsContextMap.get(parentModule.getName());
1180             typeMap = modulesByDate.get(parentModule.getRevision());
1181         }
1182
1183         if (typeMap != null) {
1184             Type type = typeMap.get(innerTypeDef);
1185             if (type instanceof GeneratedTransferObject) {
1186                 genTOBuilder.setExtendsType((GeneratedTransferObject) type);
1187             }
1188         }
1189         addUnitsToGenTO(genTOBuilder, typedef.getUnits());
1190
1191         return genTOBuilder.toInstance();
1192     }
1193
1194     /**
1195      * Finds out for each type definition how many immersion (depth) is
1196      * necessary to get to the base type. Every type definition is inserted to
1197      * the map which key is depth and value is list of type definitions with
1198      * equal depth. In next step are lists from this map concatenated to one
1199      * list in ascending order according to their depth. All type definitions
1200      * are in the list behind all type definitions on which depends.
1201      *
1202      * @param unsortedTypeDefinitions
1203      *            list of type definitions which should be sorted by depth
1204      * @return list of type definitions sorted according their each other
1205      *         dependencies (type definitions which are depend on other type
1206      *         definitions are in list behind them).
1207      */
1208     private List<TypeDefinition<?>> sortTypeDefinitionAccordingDepth(
1209             final Collection<TypeDefinition<?>> unsortedTypeDefinitions) {
1210         List<TypeDefinition<?>> sortedTypeDefinition = new ArrayList<>();
1211
1212         Map<Integer, List<TypeDefinition<?>>> typeDefinitionsDepths = new TreeMap<>();
1213         for (TypeDefinition<?> unsortedTypeDefinition : unsortedTypeDefinitions) {
1214             final int depth = getTypeDefinitionDepth(unsortedTypeDefinition);
1215             List<TypeDefinition<?>> typeDefinitionsConcreteDepth = typeDefinitionsDepths.get(depth);
1216             if (typeDefinitionsConcreteDepth == null) {
1217                 typeDefinitionsConcreteDepth = new ArrayList<TypeDefinition<?>>();
1218                 typeDefinitionsDepths.put(depth, typeDefinitionsConcreteDepth);
1219             }
1220             typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
1221         }
1222         // keys are in ascending order
1223         Set<Integer> depths = typeDefinitionsDepths.keySet();
1224         for (Integer depth : depths) {
1225             sortedTypeDefinition.addAll(typeDefinitionsDepths.get(depth));
1226         }
1227
1228         return sortedTypeDefinition;
1229     }
1230
1231     /**
1232      * Returns how many immersion is necessary to get from the type definition
1233      * to the base type.
1234      *
1235      * @param typeDefinition
1236      *            type definition for which is depth sought.
1237      * @return number of immersions which are necessary to get from the type
1238      *         definition to the base type
1239      */
1240     private int getTypeDefinitionDepth(final TypeDefinition<?> typeDefinition) {
1241         if (typeDefinition == null) {
1242             return 1;
1243         }
1244         int depth = 1;
1245         TypeDefinition<?> baseType = typeDefinition.getBaseType();
1246
1247         if (baseType instanceof ExtendedType) {
1248             depth = depth + getTypeDefinitionDepth(typeDefinition.getBaseType());
1249         } else if (baseType instanceof UnionType) {
1250             List<TypeDefinition<?>> childTypeDefinitions = ((UnionType) baseType).getTypes();
1251             int maxChildDepth = 0;
1252             int childDepth = 1;
1253             for (TypeDefinition<?> childTypeDefinition : childTypeDefinitions) {
1254                 childDepth = childDepth + getTypeDefinitionDepth(childTypeDefinition);
1255                 if (childDepth > maxChildDepth) {
1256                     maxChildDepth = childDepth;
1257                 }
1258             }
1259             return maxChildDepth;
1260         }
1261         return depth;
1262     }
1263
1264     /**
1265      * Returns string which contains the same value as <code>name</code> but
1266      * integer suffix is incremented by one. If <code>name</code> contains no
1267      * number suffix then number 1 is added.
1268      *
1269      * @param name
1270      *            string with name of augmented node
1271      * @return string with the number suffix incremented by one (or 1 is added)
1272      */
1273     private String provideAvailableNameForGenTOBuilder(String name) {
1274         Pattern searchedPattern = Pattern.compile("[0-9]+\\z");
1275         Matcher mtch = searchedPattern.matcher(name);
1276         if (mtch.find()) {
1277             final int newSuffix = Integer.valueOf(name.substring(mtch.start())) + 1;
1278             return name.substring(0, mtch.start()) + newSuffix;
1279         } else {
1280             return name + 1;
1281         }
1282     }
1283
1284     public void addUnitsToGenTO(GeneratedTOBuilder to, String units) {
1285         if (units != null && !units.isEmpty()) {
1286             to.addConstant(Types.STRING, "_UNITS", "\"" + units + "\"");
1287             GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("UNITS");
1288             prop.setReturnType(Types.STRING);
1289             to.addToStringProperty(prop);
1290         }
1291     }
1292
1293     @Override
1294     public String getTypeDefaultConstruction(LeafSchemaNode node) {
1295         return getTypeDefaultConstruction(node, node.getDefault());
1296     }
1297
1298     public String getTypeDefaultConstruction(LeafSchemaNode node, String defaultValue) {
1299         TypeDefinition<?> type = node.getType();
1300         QName typeQName = type.getQName();
1301         TypeDefinition<?> base = baseTypeDefForExtendedType(type);
1302         Preconditions.checkNotNull(type, "Cannot provide default construction for null type of " + node);
1303         Preconditions.checkNotNull(defaultValue, "Cannot provide default construction for null default statement of "
1304                 + node);
1305
1306         StringBuilder sb = new StringBuilder();
1307         String result = null;
1308         if (base instanceof BinaryTypeDefinition) {
1309             result = binaryToDef(defaultValue);
1310         } else if (base instanceof BitsTypeDefinition) {
1311             String parentName;
1312             String className;
1313             Module parent = getParentModule(node);
1314             Iterator<QName> path = node.getPath().getPathFromRoot().iterator();
1315             path.next();
1316             if (!(path.hasNext())) {
1317                 parentName = BindingMapping.getClassName((parent).getName()) + "Data";
1318                 String basePackageName = BindingGeneratorUtil.moduleNamespaceToPackageName(parent);
1319                 className = basePackageName + "." + parentName + "." + BindingMapping.getClassName(node.getQName());
1320             } else {
1321                 String basePackageName = BindingGeneratorUtil.moduleNamespaceToPackageName(parent);
1322                 String packageName = packageNameForGeneratedType(basePackageName, type.getPath());
1323                 parentName = BindingMapping.getClassName(((SchemaNode) parent).getQName());
1324                 className = packageName + "." + parentName + "." + BindingMapping.getClassName(node.getQName());
1325             }
1326             result = bitsToDef((BitsTypeDefinition) base, className, defaultValue, type instanceof ExtendedType);
1327         } else if (base instanceof BooleanTypeDefinition) {
1328             result = typeToDef(Boolean.class, defaultValue);
1329         } else if (base instanceof DecimalTypeDefinition) {
1330             result = typeToDef(BigDecimal.class, defaultValue);
1331         } else if (base instanceof EmptyTypeDefinition) {
1332             result = typeToDef(Boolean.class, defaultValue);
1333         } else if (base instanceof EnumTypeDefinition) {
1334             char[] defValArray = defaultValue.toCharArray();
1335             char first = Character.toUpperCase(defaultValue.charAt(0));
1336             defValArray[0] = first;
1337             String newDefVal = new String(defValArray);
1338             String className;
1339             if (type instanceof ExtendedType) {
1340                 Module m = getParentModule(type);
1341                 String basePackageName = BindingGeneratorUtil.moduleNamespaceToPackageName(m);
1342                 String packageName = packageNameForGeneratedType(basePackageName, type.getPath());
1343                 className = packageName + "." + BindingMapping.getClassName(typeQName);
1344             } else {
1345                 Module parentModule = getParentModule(node);
1346                 String basePackageName = BindingGeneratorUtil.moduleNamespaceToPackageName(parentModule);
1347                 String packageName = packageNameForGeneratedType(basePackageName, node.getPath());
1348                 className = packageName + "." + BindingMapping.getClassName(node.getQName());
1349             }
1350             result = className + "." + newDefVal;
1351         } else if (base instanceof IdentityrefTypeDefinition) {
1352             throw new UnsupportedOperationException("Cannot get default construction for identityref type");
1353         } else if (base instanceof InstanceIdentifierTypeDefinition) {
1354             throw new UnsupportedOperationException("Cannot get default construction for instance-identifier type");
1355         } else if (base instanceof Int8) {
1356             result = typeToDef(Byte.class, defaultValue);
1357         } else if (base instanceof Int16) {
1358             result = typeToDef(Short.class, defaultValue);
1359         } else if (base instanceof Int32) {
1360             result = typeToDef(Integer.class, defaultValue);
1361         } else if (base instanceof Int64) {
1362             result = typeToDef(Long.class, defaultValue);
1363         } else if (base instanceof LeafrefTypeDefinition) {
1364             result = leafrefToDef(node, (LeafrefTypeDefinition) base);
1365         } else if (base instanceof StringTypeDefinition) {
1366             result = "\"" + defaultValue + "\"";
1367         } else if (base instanceof Uint8) {
1368             result = typeToDef(Short.class, defaultValue);
1369         } else if (base instanceof Uint16) {
1370             result = typeToDef(Integer.class, defaultValue);
1371         } else if (base instanceof Uint32) {
1372             result = typeToDef(Long.class, defaultValue);
1373         } else if (base instanceof Uint64) {
1374             result = typeToDef(BigInteger.class, defaultValue);
1375         } else if (base instanceof UnionTypeDefinition) {
1376             result = unionToDef(node);
1377         } else {
1378             result = "";
1379         }
1380         sb.append(result);
1381
1382         if (type instanceof ExtendedType && !(base instanceof LeafrefTypeDefinition)
1383                 && !(base instanceof EnumerationType) && !(base instanceof UnionTypeDefinition)) {
1384             Module m = getParentModule(type);
1385             String basePackageName = BindingGeneratorUtil.moduleNamespaceToPackageName(m);
1386             String packageName = packageNameForGeneratedType(basePackageName, type.getPath());
1387             String className = packageName + "." + BindingMapping.getClassName(typeQName);
1388             sb.insert(0, "new " + className + "(");
1389             sb.insert(sb.length(), ")");
1390         }
1391
1392         return sb.toString();
1393     }
1394
1395     private String typeToDef(Class<?> clazz, String defaultValue) {
1396         return "new " + clazz.getName() + "(\"" + defaultValue + "\")";
1397     }
1398
1399     private String binaryToDef(String defaultValue) {
1400         StringBuilder sb = new StringBuilder();
1401         BaseEncoding en = BaseEncoding.base64();
1402         byte[] encoded = en.decode(defaultValue);
1403         sb.append("new byte[] {");
1404         for (int i = 0; i < encoded.length; i++) {
1405             sb.append(encoded[i]);
1406             if (i != encoded.length - 1) {
1407                 sb.append(", ");
1408             }
1409         }
1410         sb.append("}");
1411         return sb.toString();
1412     }
1413
1414     private String bitsToDef(BitsTypeDefinition type, String className, String defaultValue, boolean isExt) {
1415         List<Bit> bits = new ArrayList<>(type.getBits());
1416         Collections.sort(bits, new Comparator<Bit>() {
1417             @Override
1418             public int compare(Bit o1, Bit o2) {
1419                 return o1.getName().compareTo(o2.getName());
1420             }
1421         });
1422         StringBuilder sb = new StringBuilder();
1423         sb.append(isExt ? "" : "new " + className + "(");
1424         for (int i = 0; i < bits.size(); i++) {
1425             if (bits.get(i).getName().equals(defaultValue)) {
1426                 sb.append(true);
1427             } else {
1428                 sb.append(false);
1429             }
1430             if (i != bits.size() - 1) {
1431                 sb.append(", ");
1432             }
1433         }
1434         sb.append(isExt ? "" : ")");
1435         return sb.toString();
1436     }
1437
1438     private Module getParentModule(SchemaNode node) {
1439         QName qname = node.getPath().getPathFromRoot().iterator().next();
1440         URI namespace = qname.getNamespace();
1441         Date revision = qname.getRevision();
1442         return schemaContext.findModuleByNamespaceAndRevision(namespace, revision);
1443     }
1444
1445     private String leafrefToDef(LeafSchemaNode parentNode, LeafrefTypeDefinition leafrefType) {
1446         Preconditions.checkArgument(leafrefType != null, "Leafref Type Definition reference cannot be NULL!");
1447         Preconditions.checkArgument(leafrefType.getPathStatement() != null,
1448                 "The Path Statement for Leafref Type Definition cannot be NULL!");
1449
1450         final RevisionAwareXPath xpath = leafrefType.getPathStatement();
1451         final String strXPath = xpath.toString();
1452
1453         if (strXPath != null) {
1454             if (strXPath.contains("[")) {
1455                 return "new java.lang.Object()";
1456             } else {
1457                 final Module module = findParentModule(schemaContext, parentNode);
1458                 if (module != null) {
1459                     final SchemaNode dataNode;
1460                     if (xpath.isAbsolute()) {
1461                         dataNode = findDataSchemaNode(schemaContext, module, xpath);
1462                     } else {
1463                         dataNode = findDataSchemaNodeForRelativeXPath(schemaContext, module, parentNode, xpath);
1464                     }
1465                     String result = getTypeDefaultConstruction((LeafSchemaNode) dataNode, parentNode.getDefault());
1466                     return result;
1467                 }
1468             }
1469         }
1470
1471         return null;
1472     }
1473
1474     private String unionToDef(LeafSchemaNode node) {
1475         String parentName;
1476         String className;
1477
1478         if (node.getType() instanceof ExtendedType) {
1479             ExtendedType type = (ExtendedType) node.getType();
1480             QName typeQName = type.getQName();
1481             Module module = null;
1482             Set<Module> modules = schemaContext.findModuleByNamespace(typeQName.getNamespace());
1483             if (modules.size() > 1) {
1484                 for (Module m : modules) {
1485                     if (m.getRevision().equals(typeQName.getRevision())) {
1486                         module = m;
1487                         break;
1488                     }
1489                 }
1490                 if (module == null) {
1491                     List<Module> modulesList = new ArrayList<>(modules);
1492                     Collections.sort(modulesList, new Comparator<Module>() {
1493                         @Override
1494                         public int compare(Module o1, Module o2) {
1495                             return o1.getRevision().compareTo(o2.getRevision());
1496                         }
1497                     });
1498                     module = modulesList.get(0);
1499                 }
1500             } else {
1501                 module = modules.iterator().next();
1502             }
1503
1504             String basePackageName = BindingGeneratorUtil.moduleNamespaceToPackageName(module);
1505             className = basePackageName + "." + BindingMapping.getClassName(typeQName);
1506         } else {
1507             Iterator<QName> path = node.getPath().getPathFromRoot().iterator();
1508             QName first = path.next();
1509             if (!(path.hasNext())) {
1510                 URI namespace = first.getNamespace();
1511                 Date revision = first.getRevision();
1512                 Module parent = schemaContext.findModuleByNamespaceAndRevision(namespace, revision);
1513                 parentName = BindingMapping.getClassName((parent).getName()) + "Data";
1514                 String basePackageName = BindingGeneratorUtil.moduleNamespaceToPackageName(parent);
1515                 className = basePackageName + "." + parentName + "." + BindingMapping.getClassName(node.getQName());
1516             } else {
1517                 URI namespace = first.getNamespace();
1518                 Date revision = first.getRevision();
1519                 Module parentModule = schemaContext.findModuleByNamespaceAndRevision(namespace, revision);
1520                 String basePackageName = BindingGeneratorUtil.moduleNamespaceToPackageName(parentModule);
1521                 String packageName = packageNameForGeneratedType(basePackageName, node.getType().getPath());
1522                 className = packageName + "." + BindingMapping.getClassName(node.getQName());
1523             }
1524         }
1525         return union(className, node.getDefault(), node);
1526     }
1527
1528     private String union(String className, String defaultValue, LeafSchemaNode node) {
1529         StringBuilder sb = new StringBuilder();
1530         sb.append("new " + className + "(");
1531         sb.append("\"");
1532         sb.append(defaultValue);
1533         sb.append("\"");
1534         sb.append(".toCharArray()");
1535         sb.append(")");
1536         return sb.toString();
1537     }
1538
1539     @Override
1540     public String getConstructorPropertyName(SchemaNode node) {
1541         if (node instanceof TypeDefinition<?>) {
1542             return "value";
1543         } else {
1544             return "";
1545         }
1546     }
1547
1548     @Override
1549     public String getParamNameFromType(TypeDefinition<?> type) {
1550         return BindingGeneratorUtil.parseToValidParamName(type.getQName().getLocalName());
1551     }
1552
1553 }