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