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