2c41edb80b4ef86274aa392810fbae5e96c936ea
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / AbstractTypeObjectGenerator.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. 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.generator.impl.reactor;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
12 import static com.google.common.base.Verify.verifyNotNull;
13
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.Maps;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21 import java.util.stream.Collectors;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.opendaylight.mdsal.binding.generator.BindingGeneratorUtil;
25 import org.opendaylight.mdsal.binding.generator.impl.reactor.TypeReference.ResolvedLeafref;
26 import org.opendaylight.mdsal.binding.model.api.AccessModifier;
27 import org.opendaylight.mdsal.binding.model.api.ConcreteType;
28 import org.opendaylight.mdsal.binding.model.api.Enumeration;
29 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
30 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
31 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
32 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
33 import org.opendaylight.mdsal.binding.model.api.Restrictions;
34 import org.opendaylight.mdsal.binding.model.api.Type;
35 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedPropertyBuilder;
36 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
37 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
38 import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
39 import org.opendaylight.mdsal.binding.model.ri.BaseYangTypes;
40 import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
41 import org.opendaylight.mdsal.binding.model.ri.TypeConstants;
42 import org.opendaylight.mdsal.binding.model.ri.Types;
43 import org.opendaylight.mdsal.binding.model.ri.generated.type.builder.AbstractEnumerationBuilder;
44 import org.opendaylight.mdsal.binding.model.ri.generated.type.builder.GeneratedPropertyBuilderImpl;
45 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
46 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
47 import org.opendaylight.yangtools.concepts.Immutable;
48 import org.opendaylight.yangtools.yang.binding.RegexPatterns;
49 import org.opendaylight.yangtools.yang.binding.TypeObject;
50 import org.opendaylight.yangtools.yang.common.QName;
51 import org.opendaylight.yangtools.yang.common.YangConstants;
52 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
53 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
54 import org.opendaylight.yangtools.yang.model.api.stmt.BaseEffectiveStatement;
55 import org.opendaylight.yangtools.yang.model.api.stmt.LengthEffectiveStatement;
56 import org.opendaylight.yangtools.yang.model.api.stmt.PathEffectiveStatement;
57 import org.opendaylight.yangtools.yang.model.api.stmt.PatternEffectiveStatement;
58 import org.opendaylight.yangtools.yang.model.api.stmt.PatternExpression;
59 import org.opendaylight.yangtools.yang.model.api.stmt.RangeEffectiveStatement;
60 import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
61 import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
62 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
63 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
64 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
65 import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
66 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
67 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
68 import org.opendaylight.yangtools.yang.model.api.type.TypeDefinitions;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 /**
73  * Common base class for {@link TypedefGenerator} and {@link AbstractTypeAwareGenerator}. This encompasses three
74  * different statements with two different semantics:
75  * <ul>
76  *   <li>{@link TypedefGenerator}s always result in a generated {@link TypeObject}, even if the semantics is exactly
77  *       the same as its base type. This aligns with {@code typedef} defining a new type.<li>
78  *   <li>{@link LeafGenerator}s and {@link LeafListGenerator}s, on the other hand, do not generate a {@link TypeObject}
79  *       unless absolutely necassary. This aligns with {@code leaf} and {@code leaf-list} being mapped onto a property
80  *       of its parent type.<li>
81  * </ul>
82  *
83  * <p>
84  * To throw a bit of confusion into the mix, there are three exceptions to those rules:
85  * <ul>
86  *   <li>
87  *     {@code identityref} definitions never result in a type definition being emitted. The reason for this has to do
88  *     with identity type mapping as well as history of our codebase.
89  *
90  *     <p>
91  *     The problem at hand is inconsistency between the fact that identity is mapped to a {@link Class}, which is also
92  *     returned from leaves which specify it like this:
93  *     <pre>
94  *       <code>
95  *         identity iden;
96  *
97  *         container foo {
98  *           leaf foo {
99  *             type identityref {
100  *               base iden;
101  *             }
102  *           }
103  *         }
104  *       </code>
105  *     </pre>
106  *     which results in fine-looking
107  *     <pre>
108  *       <code>
109  *         interface Foo {
110  *           Class&lt;? extends Iden&gt; getFoo();
111  *         }
112  *       </code>
113  *     </pre>
114  *
115  *     <p>
116  *     This gets more dicey if we decide to extend the previous snippet to also include:
117  *     <pre>
118  *       <code>
119  *         typedef bar-ref {
120  *           type identityref {
121  *             base iden;
122  *           }
123  *         }
124  *
125  *         container bar {
126  *           leaf bar {
127  *             type bar-ref;
128  *           }
129  *         }
130  *       </code>
131  *     </pre>
132  *
133  *     <p>
134  *     Now we have competing requirements: {@code typedef} would like us to use encapsulation to capture the defined
135  *     type, while {@code getBar()} wants us to retain shape with getFoo(), as it should not matter how the
136  *     {@code identityref} was formed. We need to pick between:
137  *     <ol>
138  *       <li>
139  *         <pre>
140  *           <code>
141  *             public class BarRef extends ScalarTypeObject&lt;Class&lt;? extends Iden&gt;&gt; {
142  *               Class&lt;? extends Iden&gt; getValue() {
143  *                 // ...
144  *               }
145  *             }
146  *
147  *             interface Bar {
148  *               BarRef getBar();
149  *             }
150  *           </code>
151  *         </pre>
152  *       </li>
153  *       <li>
154  *         <pre>
155  *           <code>
156  *             interface Bar {
157  *               Class&lt;? extends Iden&gt; getBar();
158  *             }
159  *           </code>
160  *         </pre>
161  *       </li>
162  *     </ol>
163  *
164  *     <p>
165  *     Here the second option is more user-friendly, as the type system works along the lines of <b>reference</b>
166  *     semantics, treating and {@code Bar.getBar()} and {@code Foo.getFoo()} as equivalent. The first option would
167  *     force users to go through explicit encapsulation, for no added benefit as the {@code typedef} cannot possibly add
168  *     anything useful to the actual type semantics.
169  *   </li>
170  *   <li>
171  *     {@code leafref} definitions never result in a type definition being emitted. The reasons for this are similar to
172  *     {@code identityref}, but have an additional twist: a {@leafref} can target a relative path, which may only be
173  *     resolved at a particular instantiation.
174  *
175  *     Take the example of the following model:
176  *     <pre>
177  *       <code>
178  *         grouping grp {
179  *           typedef ref {
180  *             type leafref {
181  *               path ../xyzzy;
182  *             }
183  *           }
184  *
185  *           leaf foo {
186  *             type ref;
187  *           }
188  *         }
189  *
190  *         container bar {
191              uses grp;
192  *
193  *           leaf xyzzy {
194  *             type string;
195  *           }
196  *         }
197  *
198  *         container baz {
199  *           uses grp;
200  *
201  *           leaf xyzzy {
202  *             type int32;
203  *           }
204  *         }
205  *       </code>
206  *     </pre>
207  *
208  *     The {@code typedef ref} points to outside of the grouping, and hence the type of {@code leaf foo} is polymorphic:
209  *     the definition in {@code grouping grp} needs to use {@code Object}, whereas the instantiations in
210  *     {@code container bar} and {@code container baz} need to use {@code String} and {@link Integer} respectively.
211  *     Expressing the resulting interface contracts requires return type specialization and run-time checks. An
212  *     intermediate class generated for the typedef would end up being a hindrance without any benefit.
213  *   <li>
214  *   <li>
215  *     {@code enumeration} definitions never result in a derived type. This is because these are mapped to Java
216  *     {@code enum}, which does not allow subclassing.
217  *   <li>
218  * </ul>
219  *
220  * <p>
221  * At the end of the day, the mechanic translation rules are giving way to correctly mapping the semantics -- which in
222  * both of the exception cases boil down to tracking type indirection. Intermediate constructs involved in tracking
223  * type indirection in YANG constructs is therefore explicitly excluded from the generated Java code, but the Binding
224  * Specification still takes them into account when determining types as outlined above.
225  */
226 abstract class AbstractTypeObjectGenerator<S extends EffectiveStatement<?, ?>, R extends RuntimeType>
227         extends AbstractDependentGenerator<S, R> {
228     private static final class UnionDependencies implements Immutable {
229         private final Map<EffectiveStatement<?, ?>, TypeReference> identityTypes = new HashMap<>();
230         private final Map<EffectiveStatement<?, ?>, TypeReference> leafTypes = new HashMap<>();
231         private final Map<QName, TypedefGenerator> baseTypes = new HashMap<>();
232
233         UnionDependencies(final TypeEffectiveStatement<?> type, final GeneratorContext context) {
234             resolveUnionDependencies(context, type);
235         }
236
237         private void resolveUnionDependencies(final GeneratorContext context, final TypeEffectiveStatement<?> union) {
238             for (EffectiveStatement<?, ?> stmt : union.effectiveSubstatements()) {
239                 if (stmt instanceof TypeEffectiveStatement) {
240                     final TypeEffectiveStatement<?> type = (TypeEffectiveStatement<?>) stmt;
241                     final QName typeName = type.argument();
242                     if (TypeDefinitions.IDENTITYREF.equals(typeName)) {
243                         if (!identityTypes.containsKey(stmt)) {
244                             identityTypes.put(stmt, TypeReference.identityRef(
245                                 type.streamEffectiveSubstatements(BaseEffectiveStatement.class)
246                                     .map(BaseEffectiveStatement::argument)
247                                     .map(context::resolveIdentity)
248                                     .collect(Collectors.toUnmodifiableList())));
249                         }
250                     } else if (TypeDefinitions.LEAFREF.equals(typeName)) {
251                         if (!leafTypes.containsKey(stmt)) {
252                             leafTypes.put(stmt, TypeReference.leafRef(context.resolveLeafref(
253                                 type.findFirstEffectiveSubstatementArgument(PathEffectiveStatement.class)
254                                 .orElseThrow())));
255                         }
256                     } else if (TypeDefinitions.UNION.equals(typeName)) {
257                         resolveUnionDependencies(context, type);
258                     } else if (!isBuiltinName(typeName) && !baseTypes.containsKey(typeName)) {
259                         baseTypes.put(typeName, context.resolveTypedef(typeName));
260                     }
261                 }
262             }
263         }
264     }
265
266     private static final Logger LOG = LoggerFactory.getLogger(AbstractTypeObjectGenerator.class);
267     static final ImmutableMap<QName, Type> SIMPLE_TYPES = ImmutableMap.<QName, Type>builder()
268         .put(TypeDefinitions.BINARY, BaseYangTypes.BINARY_TYPE)
269         .put(TypeDefinitions.BOOLEAN, BaseYangTypes.BOOLEAN_TYPE)
270         .put(TypeDefinitions.DECIMAL64, BaseYangTypes.DECIMAL64_TYPE)
271         .put(TypeDefinitions.EMPTY, BaseYangTypes.EMPTY_TYPE)
272         .put(TypeDefinitions.INSTANCE_IDENTIFIER, BaseYangTypes.INSTANCE_IDENTIFIER)
273         .put(TypeDefinitions.INT8, BaseYangTypes.INT8_TYPE)
274         .put(TypeDefinitions.INT16, BaseYangTypes.INT16_TYPE)
275         .put(TypeDefinitions.INT32, BaseYangTypes.INT32_TYPE)
276         .put(TypeDefinitions.INT64, BaseYangTypes.INT64_TYPE)
277         .put(TypeDefinitions.STRING, BaseYangTypes.STRING_TYPE)
278         .put(TypeDefinitions.UINT8, BaseYangTypes.UINT8_TYPE)
279         .put(TypeDefinitions.UINT16, BaseYangTypes.UINT16_TYPE)
280         .put(TypeDefinitions.UINT32, BaseYangTypes.UINT32_TYPE)
281         .put(TypeDefinitions.UINT64, BaseYangTypes.UINT64_TYPE)
282         .build();
283
284     private final TypeEffectiveStatement<?> type;
285
286     // FIXME: these fields should be better-controlled with explicit sequencing guards. It it currently stands, we are
287     //        expending two (or more) additional fields to express downstream linking. If we had the concept of
288     //        resolution step (an enum), we could just get by with a simple queue of Step/Callback pairs, which would
289     //        trigger as needed. See how we manage baseGen/inferred fields.
290
291     /**
292      * The generator corresponding to our YANG base type. It produces the superclass of our encapsulated type. If it is
293      * {@code null}, this generator is the root of the hierarchy.
294      */
295     private TypedefGenerator baseGen;
296     private TypeReference refType;
297     private List<GeneratedType> auxiliaryGeneratedTypes = List.of();
298     private UnionDependencies unionDependencies;
299     private List<AbstractTypeObjectGenerator<?, ?>> inferred = List.of();
300
301     /**
302      * The type of single-element return type of the getter method associated with this generator. This is retained for
303      * run-time type purposes. It may be uninitialized, in which case this object must have a generated type.
304      */
305     private Type methodReturnTypeElement;
306
307     AbstractTypeObjectGenerator(final S statement, final AbstractCompositeGenerator<?, ?> parent) {
308         super(statement, parent);
309         type = statement().findFirstEffectiveSubstatement(TypeEffectiveStatement.class).orElseThrow();
310     }
311
312     @Override
313     public final List<GeneratedType> auxiliaryGeneratedTypes() {
314         return auxiliaryGeneratedTypes;
315     }
316
317     @Override
318     final void linkDependencies(final GeneratorContext context) {
319         verify(inferred != null, "Duplicate linking of %s", this);
320
321         final QName typeName = type.argument();
322         if (isBuiltinName(typeName)) {
323             verify(inferred.isEmpty(), "Unexpected non-empty downstreams in %s", this);
324             inferred = null;
325             return;
326         }
327
328         final AbstractExplicitGenerator<S, R> prev = previous();
329         if (prev != null) {
330             verify(prev instanceof AbstractTypeObjectGenerator, "Unexpected previous %s", prev);
331             ((AbstractTypeObjectGenerator<S, R>) prev).linkInferred(this);
332         } else {
333             linkBaseGen(context.resolveTypedef(typeName));
334         }
335     }
336
337     private void linkInferred(final AbstractTypeObjectGenerator<?, ?> downstream) {
338         if (inferred == null) {
339             downstream.linkBaseGen(verifyNotNull(baseGen, "Mismatch on linking between %s and %s", this, downstream));
340             return;
341         }
342
343         if (inferred.isEmpty()) {
344             inferred = new ArrayList<>(2);
345         }
346         inferred.add(downstream);
347     }
348
349     private void linkBaseGen(final TypedefGenerator upstreamBaseGen) {
350         verify(baseGen == null, "Attempted to replace base %s with %s in %s", baseGen, upstreamBaseGen, this);
351         final List<AbstractTypeObjectGenerator<?, ?>> downstreams = verifyNotNull(inferred,
352             "Duplicated linking of %s", this);
353         baseGen = verifyNotNull(upstreamBaseGen);
354         baseGen.addDerivedGenerator(this);
355         inferred = null;
356
357         for (AbstractTypeObjectGenerator<?, ?> downstream : downstreams) {
358             downstream.linkBaseGen(upstreamBaseGen);
359         }
360     }
361
362     void bindTypeDefinition(final GeneratorContext context) {
363         if (baseGen != null) {
364             // We have registered with baseGen, it will push the type to us
365             return;
366         }
367
368         final QName arg = type.argument();
369         if (TypeDefinitions.IDENTITYREF.equals(arg)) {
370             refType = TypeReference.identityRef(type.streamEffectiveSubstatements(BaseEffectiveStatement.class)
371                 .map(BaseEffectiveStatement::argument)
372                 .map(context::resolveIdentity)
373                 .collect(Collectors.toUnmodifiableList()));
374         } else if (TypeDefinitions.LEAFREF.equals(arg)) {
375             final AbstractTypeObjectGenerator<?, ?> targetGenerator = context.resolveLeafref(
376                 type.findFirstEffectiveSubstatementArgument(PathEffectiveStatement.class).orElseThrow());
377             checkArgument(targetGenerator != this, "Effective model contains self-referencing leaf %s",
378                 statement().argument());
379             refType = TypeReference.leafRef(targetGenerator);
380         } else if (TypeDefinitions.UNION.equals(arg)) {
381             unionDependencies = new UnionDependencies(type, context);
382             LOG.trace("Resolved union {} to dependencies {}", type, unionDependencies);
383         }
384
385         LOG.trace("Resolved base {} to generator {}", type, refType);
386         bindDerivedGenerators(refType);
387     }
388
389     final void bindTypeDefinition(final @Nullable TypeReference reference) {
390         refType = reference;
391         LOG.trace("Resolved derived {} to generator {}", type, refType);
392     }
393
394     private static boolean isBuiltinName(final QName typeName) {
395         return YangConstants.RFC6020_YANG_MODULE.equals(typeName.getModule());
396     }
397
398     abstract void bindDerivedGenerators(@Nullable TypeReference reference);
399
400     @Override
401     final ClassPlacement classPlacement() {
402         if (refType != null) {
403             // Reference types never create a new type
404             return ClassPlacement.NONE;
405         }
406         if (isDerivedEnumeration()) {
407             // Types derived from an enumeration never create a new type, as that would have to be a subclass of an enum
408             // and since enums are final, that can never happen.
409             return ClassPlacement.NONE;
410         }
411         return classPlacementImpl();
412     }
413
414     @NonNull ClassPlacement classPlacementImpl() {
415         // TODO: make this a lot more accurate by comparing the effective delta between the base type and the effective
416         //       restricted type. We should not be generating a type for constructs like:
417         //
418         //         leaf foo {
419         //           type uint8 { range 0..255; }
420         //         }
421         //
422         //       or
423         //
424         //         typedef foo {
425         //           type uint8 { range 0..100; }
426         //         }
427         //
428         //         leaf foo {
429         //           type foo { range 0..100; }
430         //         }
431         //
432         //       Which is relatively easy to do for integral types, but is way more problematic for 'pattern'
433         //       restrictions. Nevertheless we can define the mapping in a way which can be implemented with relative
434         //       ease.
435         return baseGen != null || SIMPLE_TYPES.containsKey(type.argument()) || isAddedByUses() || isAugmenting()
436             ? ClassPlacement.NONE : ClassPlacement.MEMBER;
437     }
438
439     @Override
440     final GeneratedType getGeneratedType(final TypeBuilderFactory builderFactory) {
441         // For derived enumerations defer to base type
442         return isDerivedEnumeration() ? baseGen.getGeneratedType(builderFactory)
443             : super.getGeneratedType(builderFactory);
444     }
445
446     final boolean isEnumeration() {
447         return baseGen != null ? baseGen.isEnumeration() : TypeDefinitions.ENUMERATION.equals(type.argument());
448     }
449
450     final boolean isDerivedEnumeration() {
451         return baseGen != null && baseGen.isEnumeration();
452     }
453
454     @Override
455     Type methodReturnType(final TypeBuilderFactory builderFactory) {
456         return methodReturnElementType(builderFactory);
457     }
458
459     @Override
460     final R createRuntimeType() {
461         if (methodReturnTypeElement != null) {
462             return createRuntimeType(methodReturnTypeElement);
463         }
464         final var genType = generatedType();
465         if (genType.isPresent()) {
466             return createRuntimeType(genType.orElseThrow());
467         }
468         final var prev = verifyNotNull(previous(), "No previous generator for %s", this);
469         return prev.runtimeType().orElse(null);
470     }
471
472     abstract @NonNull R createRuntimeType(Type type);
473
474     final @NonNull Type methodReturnElementType(final @NonNull TypeBuilderFactory builderFactory) {
475         var local = methodReturnTypeElement;
476         if (local == null) {
477             methodReturnTypeElement = local = createMethodReturnElementType(builderFactory);
478         }
479         return local;
480     }
481
482     private @NonNull Type createMethodReturnElementType(final @NonNull TypeBuilderFactory builderFactory) {
483         final GeneratedType generatedType = tryGeneratedType(builderFactory);
484         if (generatedType != null) {
485             // We have generated a type here, so return it. This covers 'bits', 'enumeration' and 'union'.
486             return generatedType;
487         }
488
489         if (refType != null) {
490             // This is a reference type of some kind. Defer to its judgement as to what the return type is.
491             return refType.methodReturnType(builderFactory);
492         }
493
494         final AbstractExplicitGenerator<?, ?> prev = previous();
495         if (prev != null) {
496             // We have been added through augment/uses, defer to the original definition
497             return prev.methodReturnType(builderFactory);
498         }
499
500         final Type baseType;
501         if (baseGen == null) {
502             final QName qname = type.argument();
503             baseType = verifyNotNull(SIMPLE_TYPES.get(qname), "Cannot resolve type %s in %s", qname, this);
504         } else {
505             // We are derived from a base generator. Defer to its type for return.
506             baseType = baseGen.getGeneratedType(builderFactory);
507         }
508
509         return restrictType(baseType, computeRestrictions(), builderFactory);
510     }
511
512     private static @NonNull Type restrictType(final @NonNull Type baseType, final Restrictions restrictions,
513             final TypeBuilderFactory builderFactory) {
514         if (restrictions == null || restrictions.isEmpty()) {
515             // No additional restrictions, return base type
516             return baseType;
517         }
518
519         if (!(baseType instanceof GeneratedTransferObject)) {
520             // This is a simple Java type, just wrap it with new restrictions
521             return Types.restrictedType(baseType, restrictions);
522         }
523
524         // Base type is a GTO, we need to re-adjust it with new restrictions
525         final GeneratedTransferObject gto = (GeneratedTransferObject) baseType;
526         final GeneratedTOBuilder builder = builderFactory.newGeneratedTOBuilder(gto.getIdentifier());
527         final GeneratedTransferObject parent = gto.getSuperType();
528         if (parent != null) {
529             builder.setExtendsType(parent);
530         }
531         builder.setRestrictions(restrictions);
532         for (GeneratedProperty gp : gto.getProperties()) {
533             builder.addProperty(gp.getName())
534                 .setValue(gp.getValue())
535                 .setReadOnly(gp.isReadOnly())
536                 .setAccessModifier(gp.getAccessModifier())
537                 .setReturnType(gp.getReturnType())
538                 .setFinal(gp.isFinal())
539                 .setStatic(gp.isStatic());
540         }
541         return builder.build();
542     }
543
544     @Override
545     final void addAsGetterMethodOverride(final GeneratedTypeBuilderBase<?> builder,
546             final TypeBuilderFactory builderFactory) {
547         if (!(refType instanceof ResolvedLeafref)) {
548             // We are not dealing with a leafref or have nothing to add
549             return;
550         }
551
552         final AbstractTypeObjectGenerator<?, ?> prev =
553             (AbstractTypeObjectGenerator<?, ?>) verifyNotNull(previous(), "Missing previous link in %s", this);
554         if (prev.refType instanceof ResolvedLeafref) {
555             // We should be already inheriting the correct type
556             return;
557         }
558
559         // Note: this may we wrapped for leaf-list, hence we need to deal with that
560         final Type myType = methodReturnType(builderFactory);
561         LOG.trace("Override of {} to {}", this, myType);
562         final MethodSignatureBuilder getter = constructGetter(builder, myType);
563         getter.addAnnotation(OVERRIDE_ANNOTATION);
564         annotateDeprecatedIfNecessary(getter);
565     }
566
567     final @Nullable Restrictions computeRestrictions() {
568         final List<ValueRange> length = type.findFirstEffectiveSubstatementArgument(LengthEffectiveStatement.class)
569             .orElse(List.of());
570         final List<ValueRange> range = type.findFirstEffectiveSubstatementArgument(RangeEffectiveStatement.class)
571             .orElse(List.of());
572         final List<PatternExpression> patterns = type.streamEffectiveSubstatements(PatternEffectiveStatement.class)
573             .map(PatternEffectiveStatement::argument)
574             .collect(Collectors.toUnmodifiableList());
575
576         if (length.isEmpty() && range.isEmpty() && patterns.isEmpty()) {
577             return null;
578         }
579
580         return BindingGeneratorUtil.getRestrictions(extractTypeDefinition());
581     }
582
583     @Override
584     final GeneratedType createTypeImpl(final TypeBuilderFactory builderFactory) {
585         if (baseGen != null) {
586             final GeneratedType baseType = baseGen.getGeneratedType(builderFactory);
587             verify(baseType instanceof GeneratedTransferObject, "Unexpected base type %s", baseType);
588             return createDerivedType(builderFactory, (GeneratedTransferObject) baseType);
589         }
590
591         // FIXME: why do we need this boolean?
592         final boolean isTypedef = this instanceof TypedefGenerator;
593         final QName arg = type.argument();
594         if (TypeDefinitions.BITS.equals(arg)) {
595             return createBits(builderFactory, typeName(), currentModule(), extractTypeDefinition(), isTypedef);
596         } else if (TypeDefinitions.ENUMERATION.equals(arg)) {
597             return createEnumeration(builderFactory, typeName(), currentModule(),
598                 (EnumTypeDefinition) extractTypeDefinition());
599         } else if (TypeDefinitions.UNION.equals(arg)) {
600             final List<GeneratedType> tmp = new ArrayList<>(1);
601             final GeneratedTransferObject ret = createUnion(tmp, builderFactory, statement(), unionDependencies,
602                 typeName(), currentModule(), type, isTypedef, extractTypeDefinition());
603             auxiliaryGeneratedTypes = List.copyOf(tmp);
604             return ret;
605         } else {
606             return createSimple(builderFactory, typeName(), currentModule(),
607                 verifyNotNull(SIMPLE_TYPES.get(arg), "Unhandled type %s", arg), extractTypeDefinition());
608         }
609     }
610
611     private static @NonNull GeneratedTransferObject createBits(final TypeBuilderFactory builderFactory,
612             final JavaTypeName typeName, final ModuleGenerator module, final TypeDefinition<?> typedef,
613             final boolean isTypedef) {
614         final GeneratedTOBuilder builder = builderFactory.newGeneratedTOBuilder(typeName);
615         builder.setTypedef(isTypedef);
616         builder.addImplementsType(BindingTypes.TYPE_OBJECT);
617         builder.setBaseType(typedef);
618
619         for (Bit bit : ((BitsTypeDefinition) typedef).getBits()) {
620             final String name = bit.getName();
621             GeneratedPropertyBuilder genPropertyBuilder = builder.addProperty(BindingMapping.getPropertyName(name));
622             genPropertyBuilder.setReadOnly(true);
623             genPropertyBuilder.setReturnType(BaseYangTypes.BOOLEAN_TYPE);
624
625             builder.addEqualsIdentity(genPropertyBuilder);
626             builder.addHashIdentity(genPropertyBuilder);
627             builder.addToStringProperty(genPropertyBuilder);
628         }
629
630         // builder.setSchemaPath(typedef.getPath());
631         builder.setModuleName(module.statement().argument().getLocalName());
632         builderFactory.addCodegenInformation(typedef, builder);
633         annotateDeprecatedIfNecessary(typedef, builder);
634         makeSerializable(builder);
635         return builder.build();
636     }
637
638     private static @NonNull Enumeration createEnumeration(final TypeBuilderFactory builderFactory,
639             final JavaTypeName typeName, final ModuleGenerator module, final EnumTypeDefinition typedef) {
640         // TODO units for typedef enum
641         final AbstractEnumerationBuilder builder = builderFactory.newEnumerationBuilder(typeName);
642
643         typedef.getDescription().map(BindingGeneratorUtil::encodeAngleBrackets)
644             .ifPresent(builder::setDescription);
645         typedef.getReference().ifPresent(builder::setReference);
646
647         builder.setModuleName(module.statement().argument().getLocalName());
648         builder.updateEnumPairsFromEnumTypeDef(typedef);
649         return builder.toInstance();
650     }
651
652     private static @NonNull GeneratedType createSimple(final TypeBuilderFactory builderFactory,
653             final JavaTypeName typeName, final ModuleGenerator module, final Type javaType,
654             final TypeDefinition<?> typedef) {
655         final String moduleName = module.statement().argument().getLocalName();
656         final GeneratedTOBuilder builder = builderFactory.newGeneratedTOBuilder(typeName);
657         builder.setTypedef(true);
658         builder.addImplementsType(BindingTypes.scalarTypeObject(javaType));
659
660         final GeneratedPropertyBuilder genPropBuilder = builder.addProperty(TypeConstants.VALUE_PROP);
661         genPropBuilder.setReturnType(javaType);
662         builder.addEqualsIdentity(genPropBuilder);
663         builder.addHashIdentity(genPropBuilder);
664         builder.addToStringProperty(genPropBuilder);
665
666         builder.setRestrictions(BindingGeneratorUtil.getRestrictions(typedef));
667
668 //        builder.setSchemaPath(typedef.getPath());
669         builder.setModuleName(moduleName);
670         builderFactory.addCodegenInformation(typedef, builder);
671
672         annotateDeprecatedIfNecessary(typedef, builder);
673
674         if (javaType instanceof ConcreteType
675             // FIXME: This looks very suspicious: we should by checking for Types.STRING
676             && "String".equals(javaType.getName()) && typedef.getBaseType() != null) {
677             addStringRegExAsConstant(builder, resolveRegExpressions(typedef));
678         }
679         addUnits(builder, typedef);
680
681         makeSerializable(builder);
682         return builder.build();
683     }
684
685     private static @NonNull GeneratedTransferObject createUnion(final List<GeneratedType> auxiliaryGeneratedTypes,
686             final TypeBuilderFactory builderFactory, final EffectiveStatement<?, ?> definingStatement,
687             final UnionDependencies dependencies, final JavaTypeName typeName, final ModuleGenerator module,
688             final TypeEffectiveStatement<?> type, final boolean isTypedef, final TypeDefinition<?> typedef) {
689         final GeneratedUnionBuilder builder = builderFactory.newGeneratedUnionBuilder(typeName);
690         builder.addImplementsType(BindingTypes.TYPE_OBJECT);
691         builder.setIsUnion(true);
692
693 //        builder.setSchemaPath(typedef.getPath());
694         builder.setModuleName(module.statement().argument().getLocalName());
695         builderFactory.addCodegenInformation(definingStatement, builder);
696
697         annotateDeprecatedIfNecessary(definingStatement, builder);
698
699         // Pattern string is the key, XSD regex is the value. The reason for this choice is that the pattern carries
700         // also negation information and hence guarantees uniqueness.
701         final Map<String, String> expressions = new HashMap<>();
702
703         // Linear list of properties generated from subtypes. We need this information for runtime types, as it allows
704         // direct mapping of type to corresponding property -- without having to resort to re-resolving the leafrefs
705         // again.
706         final List<String> typeProperties = new ArrayList<>();
707
708         for (EffectiveStatement<?, ?> stmt : type.effectiveSubstatements()) {
709             if (stmt instanceof TypeEffectiveStatement) {
710                 final TypeEffectiveStatement<?> subType = (TypeEffectiveStatement<?>) stmt;
711                 final QName subName = subType.argument();
712                 final String localName = subName.getLocalName();
713
714                 String propSource = localName;
715                 final Type generatedType;
716                 if (TypeDefinitions.UNION.equals(subName)) {
717                     final JavaTypeName subUnionName = typeName.createEnclosed(
718                         provideAvailableNameForGenTOBuilder(typeName.simpleName()));
719                     final GeneratedTransferObject subUnion = createUnion(auxiliaryGeneratedTypes, builderFactory,
720                         definingStatement, dependencies, subUnionName, module, subType, isTypedef,
721                         subType.getTypeDefinition());
722                     builder.addEnclosingTransferObject(subUnion);
723                     propSource = subUnionName.simpleName();
724                     generatedType = subUnion;
725                 } else if (TypeDefinitions.ENUMERATION.equals(subName)) {
726                     final Enumeration subEnumeration = createEnumeration(builderFactory,
727                         typeName.createEnclosed(BindingMapping.getClassName(localName), "$"), module,
728                         (EnumTypeDefinition) subType.getTypeDefinition());
729                     builder.addEnumeration(subEnumeration);
730                     generatedType = subEnumeration;
731                 } else if (TypeDefinitions.BITS.equals(subName)) {
732                     final GeneratedTransferObject subBits = createBits(builderFactory,
733                         typeName.createEnclosed(BindingMapping.getClassName(localName), "$"), module,
734                         subType.getTypeDefinition(), isTypedef);
735                     builder.addEnclosingTransferObject(subBits);
736                     generatedType = subBits;
737                 } else if (TypeDefinitions.IDENTITYREF.equals(subName)) {
738                     generatedType = verifyNotNull(dependencies.identityTypes.get(stmt),
739                         "Cannot resolve identityref %s in %s", stmt, definingStatement)
740                         .methodReturnType(builderFactory);
741                 } else if (TypeDefinitions.LEAFREF.equals(subName)) {
742                     generatedType = verifyNotNull(dependencies.leafTypes.get(stmt),
743                         "Cannot resolve leafref %s in %s", stmt, definingStatement)
744                         .methodReturnType(builderFactory);
745                 } else {
746                     Type baseType = SIMPLE_TYPES.get(subName);
747                     if (baseType == null) {
748                         // This has to be a reference to a typedef, let's lookup it up and pick up its type
749                         final AbstractTypeObjectGenerator<?, ?> baseGen = verifyNotNull(
750                             dependencies.baseTypes.get(subName), "Cannot resolve base type %s in %s", subName,
751                             definingStatement);
752                         baseType = baseGen.methodReturnType(builderFactory);
753
754                         // FIXME: This is legacy behaviour for leafrefs:
755                         if (baseGen.refType instanceof TypeReference.Leafref) {
756                             // if there already is a compatible property, do not generate a new one
757                             final Type search = baseType;
758
759                             final String matching = builder.getProperties().stream()
760                                 .filter(prop -> search == ((GeneratedPropertyBuilderImpl) prop).getReturnType())
761                                 .findFirst()
762                                 .map(GeneratedPropertyBuilder::getName)
763                                 .orElse(null);
764                             if (matching != null) {
765                                 typeProperties.add(matching);
766                                 continue;
767                             }
768
769                             // ... otherwise generate this weird property name
770                             propSource = BindingMapping.getUnionLeafrefMemberName(builder.getName(),
771                                 baseType.getName());
772                         }
773                     }
774
775                     expressions.putAll(resolveRegExpressions(subType.getTypeDefinition()));
776
777                     generatedType = restrictType(baseType,
778                         BindingGeneratorUtil.getRestrictions(type.getTypeDefinition()), builderFactory);
779                 }
780
781                 final String propName = BindingMapping.getPropertyName(propSource);
782                 typeProperties.add(propName);
783
784                 if (builder.containsProperty(propName)) {
785                     /*
786                      *  FIXME: this is not okay, as we are ignoring multiple base types. For example in the case of:
787                      *
788                      *    type union {
789                      *      type string {
790                      *        length 1..5;
791                      *      }
792                      *      type string {
793                      *        length 8..10;
794                      *      }
795                      *    }
796                      *
797                      *  We are ending up losing the information about 8..10 being an alternative. This is also the case
798                      *  for leafrefs -- we are performing property compression as well (see above). While it is alluring
799                      *  to merge these into 'length 1..5|8..10', that may not be generally feasible.
800                      *
801                      *  We should resort to a counter of conflicting names, i.e. the second string would be mapped to
802                      *  'string1' or similar.
803                      */
804                     continue;
805                 }
806
807                 final GeneratedPropertyBuilder propBuilder = builder
808                     .addProperty(propName)
809                     .setReturnType(generatedType);
810
811                 builder.addEqualsIdentity(propBuilder);
812                 builder.addHashIdentity(propBuilder);
813                 builder.addToStringProperty(propBuilder);
814             }
815         }
816
817         // Record property names if needed
818         builder.setTypePropertyNames(typeProperties);
819
820         addStringRegExAsConstant(builder, expressions);
821         addUnits(builder, typedef);
822
823         makeSerializable(builder);
824         final GeneratedTransferObject ret = builder.build();
825
826         // Define a corresponding union builder. Typedefs are always anchored at a Java package root,
827         // so we are placing the builder alongside the union.
828         final GeneratedTOBuilder unionBuilder = builderFactory.newGeneratedTOBuilder(unionBuilderName(typeName));
829         unionBuilder.setIsUnionBuilder(true);
830         unionBuilder.addMethod("getDefaultInstance")
831             .setAccessModifier(AccessModifier.PUBLIC)
832             .setStatic(true)
833             .setReturnType(ret)
834             .addParameter(Types.STRING, "defaultValue");
835         auxiliaryGeneratedTypes.add(unionBuilder.build());
836
837         return ret;
838     }
839
840     // FIXME: this can be a source of conflicts as we are not guarding against nesting
841     private static @NonNull JavaTypeName unionBuilderName(final JavaTypeName unionName) {
842         final StringBuilder sb = new StringBuilder();
843         for (String part : unionName.localNameComponents()) {
844             sb.append(part);
845         }
846         return JavaTypeName.create(unionName.packageName(), sb.append(BindingMapping.BUILDER_SUFFIX).toString());
847     }
848
849     // FIXME: we should not rely on TypeDefinition
850     abstract @NonNull TypeDefinition<?> extractTypeDefinition();
851
852     abstract @NonNull GeneratedTransferObject createDerivedType(@NonNull TypeBuilderFactory builderFactory,
853         @NonNull GeneratedTransferObject baseType);
854
855     /**
856      * Adds to the {@code genTOBuilder} the constant which contains regular expressions from the {@code expressions}.
857      *
858      * @param genTOBuilder generated TO builder to which are {@code regular expressions} added
859      * @param expressions list of string which represent regular expressions
860      */
861     static void addStringRegExAsConstant(final GeneratedTOBuilder genTOBuilder, final Map<String, String> expressions) {
862         if (!expressions.isEmpty()) {
863             genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), TypeConstants.PATTERN_CONSTANT_NAME,
864                 ImmutableMap.copyOf(expressions));
865         }
866     }
867
868     /**
869      * Converts the pattern constraints from {@code typedef} to the list of the strings which represents these
870      * constraints.
871      *
872      * @param typedef extended type in which are the pattern constraints sought
873      * @return list of strings which represents the constraint patterns
874      * @throws IllegalArgumentException if <code>typedef</code> equals null
875      */
876     static Map<String, String> resolveRegExpressions(final TypeDefinition<?> typedef) {
877         return typedef instanceof StringTypeDefinition
878             // TODO: run diff against base ?
879             ? resolveRegExpressions(((StringTypeDefinition) typedef).getPatternConstraints())
880                 : ImmutableMap.of();
881     }
882
883     /**
884      * Converts the pattern constraints to the list of the strings which represents these constraints.
885      *
886      * @param patternConstraints list of pattern constraints
887      * @return list of strings which represents the constraint patterns
888      */
889     private static Map<String, String> resolveRegExpressions(final List<PatternConstraint> patternConstraints) {
890         if (patternConstraints.isEmpty()) {
891             return ImmutableMap.of();
892         }
893
894         final Map<String, String> regExps = Maps.newHashMapWithExpectedSize(patternConstraints.size());
895         for (PatternConstraint patternConstraint : patternConstraints) {
896             String regEx = patternConstraint.getJavaPatternString();
897
898             // The pattern can be inverted
899             final Optional<ModifierKind> optModifier = patternConstraint.getModifier();
900             if (optModifier.isPresent()) {
901                 regEx = applyModifier(optModifier.get(), regEx);
902             }
903
904             regExps.put(regEx, patternConstraint.getRegularExpressionString());
905         }
906
907         return regExps;
908     }
909
910     /**
911      * Returns string which contains the same value as <code>name</code> but integer suffix is incremented by one. If
912      * <code>name</code> contains no number suffix, a new suffix initialized at 1 is added. A suffix is actually
913      * composed of a '$' marker, which is safe, as no YANG identifier can contain '$', and a unsigned decimal integer.
914      *
915      * @param name string with name of augmented node
916      * @return string with the number suffix incremented by one (or 1 is added)
917      */
918     private static String provideAvailableNameForGenTOBuilder(final String name) {
919         final int dollar = name.indexOf('$');
920         if (dollar == -1) {
921             return name + "$1";
922         }
923
924         final int newSuffix = Integer.parseUnsignedInt(name.substring(dollar + 1)) + 1;
925         verify(newSuffix > 0, "Suffix counter overflow");
926         return name.substring(0, dollar + 1) + newSuffix;
927     }
928
929     private static String applyModifier(final ModifierKind modifier, final String pattern) {
930         switch (modifier) {
931             case INVERT_MATCH:
932                 return RegexPatterns.negatePatternString(pattern);
933             default:
934                 LOG.warn("Ignoring unhandled modifier {}", modifier);
935                 return pattern;
936         }
937     }
938 }